Springboot详解整合SpringSecurity实现全过程
作者:kaico2018
使用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(); }
到此这篇关于Springboot详解整合SpringSecurity实现全过程的文章就介绍到这了,更多相关Springboot SpringSecurity内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
- Springboot安全框架整合SpringSecurity实现方式
- springboot整合security和vue的实践
- springboot整合springsecurity与mybatis-plus的简单实现
- SpringBoot如何整合Springsecurity实现数据库登录及权限控制
- SpringBoot整合SpringSecurityOauth2实现鉴权动态权限问题
- SpringBoot整合SpringSecurity实现JWT认证的项目实践
- SpringBoot整合Security权限控制登录首页
- SpringBoot整合Spring Security过滤器链加载执行流程源码分析(最新推荐)
- SpringBoot整合SpringSecurity和JWT和Redis实现统一鉴权认证
- SpringBoot整合SpringSecurity认证与授权
- SpringBoot整合Spring Security构建安全的Web应用