Springboot SpringSecurity
kaico2018 人气:0使用Basic认证模式
1、maven依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <dependencies> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- springboot整合freemarker --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <!-->spring-boot 整合security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies>
2、SecurityConfig 配置类
@Component @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 添加授权账户 * * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 设置用户账号信息和权限 auth.inMemoryAuthentication().withUser("kaico_admin").password("kaico") .authorities("/"); // 如果kaico_admin账户权限的情况 所有的接口都可以访问,如果kaico_add 只能访问addMember auth.inMemoryAuthentication().withUser("kaico_add").password("kaico") .authorities("/"); } @Override protected void configure(HttpSecurity http) throws Exception { //配置httpBasic Http协议认证 http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().httpBasic(); } /** * There is no PasswordEncoder mapped for the id "null" * 原因:升级为Security5.0以上密码支持多中加密方式,恢复以前模式 * * @return */ @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } }
3、测试controller接口
@Controller public class IndexController { /** * 跳转到首页 * * @return */ @RequestMapping("/") public String index() { return "index"; } @ResponseBody @RequestMapping("/addMember") public String addMember() { return "新增用户"; } @ResponseBody @RequestMapping("/delMember") public String delMember() { return "删除用户"; } @ResponseBody @RequestMapping("/updateMember") public String updateMember() { return "修改用户"; } @ResponseBody @RequestMapping("/showMember") public String showMember() { return "查询用户"; } }
使用form表形式登录
在上面Basic认证模式的基础上修改SecurityConfig
配置类,修改下面的方法
@Override protected void configure(HttpSecurity http) throws Exception { //配置httpBasic Http协议认证 http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().formLogin(); }
springSecurity默认提供登录页面,如下图所示:
注意:使用表单登录之后,登录成功之后默认跳转首页,也就是请求路径/
,没有该请求路径的话会报错。
实现权限控制
上面的案例只是实现了登录认证,但是还没有实现权限控制。
在上面的基础上修改
1、修改SecurityConfig
配置类
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 设置用户账号信息和权限 auth.inMemoryAuthentication().withUser("kaico_admin").password("kaico") .authorities("addMember", "delMember", "updateMember", "showMember" ); // 如果kaico_admin账户权限的情况 所有的接口都可以访问,如果kaico_add 只能访问addMember auth.inMemoryAuthentication().withUser("kaico_add").password("kaico") .authorities("addMember"); } @Override protected void configure(HttpSecurity http) throws Exception { //配置权限 http.authorizeRequests().antMatchers("/addMember").hasAnyAuthority("addMember") .antMatchers("/addMember").hasAnyAuthority("addMember") .antMatchers("/delMember").hasAnyAuthority("delMember") .antMatchers("/updateMember").hasAnyAuthority("updateMember") .antMatchers("/showMember").hasAnyAuthority("showMember") .antMatchers("/**").fullyAuthenticated().and().formLogin(); }
没有权限报403错误
修改403权限不足页面
1、增加配置类
@Configuration public class WebServerAutoConfiguration { @Bean public ConfigurableServletWebServerFactory webServerFactory() { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400"); ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401"); ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/error/403"); ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"); ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/error/415"); ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"); factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500); return factory; } }
2、增加对应的错误请求路径
@RestController public class ErrorController { @RequestMapping("/error/403") public String error403(){ return "您当前访问的接口权限不足"; } }
自定义登录页面
1、登录页面准备
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <h1>权限控制登陆系统</h1> <form action="/login" method="post"> <span>用户名称</span><input type="text" name="username"/> <br> <span>用户密码</span><input type="password" name="password"/> <br> <input type="submit" value="登陆"> </form> <#if RequestParameters['error']??> 用户名称或者密码错误 </#if> </body> </html>
2、修改SecurityConfig
配置类
@Override protected void configure(HttpSecurity http) throws Exception { //配置权限 http.authorizeRequests().antMatchers("/addMember").hasAnyAuthority("addMember") .antMatchers("/addMember").hasAnyAuthority("addMember") .antMatchers("/delMember").hasAnyAuthority("delMember") .antMatchers("/updateMember").hasAnyAuthority("updateMember") .antMatchers("/showMember").hasAnyAuthority("showMember") .antMatchers("/login").permitAll() //放行登录请求页面 .antMatchers("/**").fullyAuthenticated() .and().formLogin().loginPage("/login").and().csrf().disable(); }
3、增加登录页面请求controller
@Controller public class LoginController { @RequestMapping("/login") public String login(){ return "login"; } }
结合数据库实现RBAC权限模型权限控制
环境准备
数据库
RBAC模型相关数据库表关系图
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for sys_permission -- ---------------------------- DROP TABLE IF EXISTS `sys_permission`; CREATE TABLE `sys_permission` ( `id` int(10) NOT NULL, `permName` varchar(50) DEFAULT NULL, `permTag` varchar(50) DEFAULT NULL, `url` varchar(255) DEFAULT NULL COMMENT '请求url', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_permission -- ---------------------------- INSERT INTO `sys_permission` VALUES ('1', '查询用户', 'showMember', '/showMember'); INSERT INTO `sys_permission` VALUES ('2', '添加用户', 'addMember', '/addMember'); INSERT INTO `sys_permission` VALUES ('3', '修改用户', 'updateMember', '/updateMember'); INSERT INTO `sys_permission` VALUES ('4', '删除用户', 'delMember', '/delMember'); -- ---------------------------- -- Table structure for sys_role -- ---------------------------- DROP TABLE IF EXISTS `sys_role`; CREATE TABLE `sys_role` ( `id` int(10) NOT NULL, `roleName` varchar(50) DEFAULT NULL, `roleDesc` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_role -- ---------------------------- INSERT INTO `sys_role` VALUES ('1', 'admin', '管理员'); INSERT INTO `sys_role` VALUES ('2', 'add_user', '添加管理员'); -- ---------------------------- -- Table structure for sys_role_permission -- ---------------------------- DROP TABLE IF EXISTS `sys_role_permission`; CREATE TABLE `sys_role_permission` ( `role_id` int(10) DEFAULT NULL, `perm_id` int(10) DEFAULT NULL, KEY `FK_Reference_3` (`role_id`), KEY `FK_Reference_4` (`perm_id`), CONSTRAINT `FK_Reference_4` FOREIGN KEY (`perm_id`) REFERENCES `sys_permission` (`id`), CONSTRAINT `FK_Reference_3` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_role_permission -- ---------------------------- INSERT INTO `sys_role_permission` VALUES ('1', '1'); INSERT INTO `sys_role_permission` VALUES ('1', '2'); INSERT INTO `sys_role_permission` VALUES ('1', '3'); INSERT INTO `sys_role_permission` VALUES ('1', '4'); INSERT INTO `sys_role_permission` VALUES ('2', '2'); -- ---------------------------- -- Table structure for sys_user -- ---------------------------- DROP TABLE IF EXISTS `sys_user`; CREATE TABLE `sys_user` ( `id` int(10) NOT NULL, `username` varchar(50) DEFAULT NULL, `realname` varchar(50) DEFAULT NULL, `password` varchar(50) DEFAULT NULL, `createDate` date DEFAULT NULL, `lastLoginTime` date DEFAULT NULL, `enabled` int(5) DEFAULT NULL, `accountNonExpired` int(5) DEFAULT NULL, `accountNonLocked` int(5) DEFAULT NULL, `credentialsNonExpired` int(5) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_user -- ---------------------------- INSERT INTO `sys_user` VALUES ('1', 'kaico_admin', '张三', 'c2c92733a75333a4bac673632ff7519a', '2018-11-13', '2018-11-13', '1', '1', '1', '1'); INSERT INTO `sys_user` VALUES ('2', 'kaico_add', '小余', 'c2c92733a75333a4bac673632ff7519a', '2018-11-13', '2018-11-13', '1', '1', '1', '1'); -- ---------------------------- -- Table structure for sys_user_role -- ---------------------------- DROP TABLE IF EXISTS `sys_user_role`; CREATE TABLE `sys_user_role` ( `user_id` int(10) DEFAULT NULL, `role_id` int(10) DEFAULT NULL, KEY `FK_Reference_1` (`user_id`), KEY `FK_Reference_2` (`role_id`), CONSTRAINT `FK_Reference_2` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`), CONSTRAINT `FK_Reference_1` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_user_role -- ---------------------------- INSERT INTO `sys_user_role` VALUES ('1', '1'); INSERT INTO `sys_user_role` VALUES ('2', '2');
maven依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <dependencies> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- springboot整合freemarker --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <!-->spring-boot 整合security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- springboot 整合mybatis框架 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!-- alibaba的druid数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
yml配置
# 配置freemarker
spring:
freemarker:
# 设置模板后缀名
suffix: .ftl
# 设置文档类型
content-type: text/html
# 设置页面编码格式
charset: UTF-8
# 设置页面缓存
cache: false
# 设置ftl文件路径
template-loader-path:
- classpath:/templates
# 设置静态文件路径,js,css等
mvc:
static-path-pattern: /static/**
datasource:
name: test
url: jdbc:mysql://www.kaicostudy.com:3306/study_springSecurity
username: root
password: 123456
# druid 连接池
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
java代码
实体类
// 用户信息表 @Data public class UserEntity implements UserDetails { private Integer id; private String username; private String realname; private String password; private Date createDate; private Date lastLoginTime; private boolean enabled; private boolean accountNonExpired; private boolean accountNonLocked; private boolean credentialsNonExpired; // 用户所有权限 private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } }
// 角色信息表 @Data public class RoleEntity { private Integer id; private String roleName; private String roleDesc; }
@Data public class PermissionEntity { private Integer id; // 权限名称 private String permName; // 权限标识 private String permTag; // 请求url private String url; }
mapper类
@Mapper public interface PermissionMapper { @Select(" select * from sys_permission ") List<PermissionEntity> findAllPermission(); }
@Mapper public interface UserMapper { /** * 根据用户名称查询 * * @param userName * @return */ @Select(" select * from sys_user where username = #{userName}") UserEntity findByUsername(@Param("userName") String userName); /** * 查询用户的权限根据用户查询权限 * * @param userName * @return */ @Select(" select permission.* from sys_user user" + " inner join sys_user_role user_role" + " on user.id = user_role.user_id inner join " + "sys_role_permission role_permission on user_role.role_id = role_permission.role_id " + " inner join sys_permission permission on role_permission.perm_id = permission.id where user.username = #{userName};") List<PermissionEntity> findPermissionByUsername(@Param("userName") String userName); }
实现springSecurity的接口UserDetailsService
@Component @Slf4j public class MemberUserDetailsService implements UserDetailsService { @Autowired private UserMapper userMapper; /** * loadUserByUserName * * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 1.根据该用户名称查询在数据库中是否存在 UserEntity userEntity = userMapper.findByUsername(username); if (userEntity == null) { return null; } // 2.查询对应的用户权限 List<PermissionEntity> listPermission = userMapper.findPermissionByUsername(username); List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); listPermission.forEach(user -> { authorities.add(new SimpleGrantedAuthority(user.getPermTag())); }); log.info(">>>authorities:{}<<<", authorities); // 3.将该权限添加到security userEntity.setAuthorities(authorities); return userEntity; } }
密码加密工具类
public class MD5Util { private static final String SALT = "kaico"; public static String encode(String password) { password = password + SALT; MessageDigest md5 = null; try { md5 = MessageDigest.getInstance("MD5"); } catch (Exception e) { throw new RuntimeException(e); } char[] charArray = password.toCharArray(); byte[] byteArray = new byte[charArray.length]; for (int i = 0; i < charArray.length; i++) byteArray[i] = (byte) charArray[i]; byte[] md5Bytes = md5.digest(byteArray); StringBuffer hexValue = new StringBuffer(); for (int i = 0; i < md5Bytes.length; i++) { int val = ((int) md5Bytes[i]) & 0xff; if (val < 16) { hexValue.append("0"); } hexValue.append(Integer.toHexString(val)); } return hexValue.toString(); } }
SecurityConfig配置类
@Component @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MemberUserDetailsService memberUserDetailsService; /** * 添加授权账户 * * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 设置用户账号信息和权限 auth.userDetailsService(memberUserDetailsService).passwordEncoder(new PasswordEncoder() { @Override public String encode(CharSequence rawPassword) { //对密码做加密 return MD5Util.encode((String) rawPassword); } /** * * @param charSequence 用户输入的密码 * @param s 数据库字段中加密好的密码 * @return */ @Override public boolean matches(CharSequence charSequence, String s) { String rawPass = MD5Util.encode((String) charSequence); boolean result = rawPass.equals(s); return result; } }); } @Override protected void configure(HttpSecurity http) throws Exception { //配置权限 http.authorizeRequests().antMatchers("/addMember").hasAnyAuthority("addMember") .antMatchers("/addMember").hasAnyAuthority("addMember") .antMatchers("/delMember").hasAnyAuthority("delMember") .antMatchers("/updateMember").hasAnyAuthority("updateMember") .antMatchers("/showMember").hasAnyAuthority("showMember") .antMatchers("/login").permitAll() //放行登录请求页面 .antMatchers("/**").fullyAuthenticated() .and().formLogin().loginPage("/login").and().csrf().disable(); } }
整合完成,开始测试。
动态绑定数据库所有权限
在上面代码的基础上实现,上面是在代码中配置请求路径和权限标识的绑定,现在改成在数据库中动态配置。
SecurityConfig配置类
@Override protected void configure(HttpSecurity http) throws Exception { //配置权限 List<PermissionEntity> allPermission = permissionMapper.findAllPermission(); ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry = http.authorizeRequests(); allPermission.forEach((permission) -> { expressionInterceptUrlRegistry.antMatchers(permission.getUrl()). hasAnyAuthority(permission.getPermTag()); }); expressionInterceptUrlRegistry.antMatchers("/login").permitAll() .antMatchers("/**").fullyAuthenticated() .and().formLogin().loginPage("/login").and().csrf().disable(); }
加载全部内容