开发基础
开发基础笔记
mybatis-plus通用的service方法
通过jenkins配置前后端自动打包及发布
mybatis-plus实现分页的方式
Java极客技术公众号PDF
狂神说SpringBoot
Hashids java 版使用
SpringBoot
1、Web快速开发
2、结果集的封装
3、集成MyBatis实现数据库操作
4、Springboot @Validated参数校验
5、SpringBoot全局异常处理
6、拦截器HandlerInterceptor
7、集成Swagger实现API自动生成
8、集成knife4j实现API自动生成
9、Springboot集成MyBatis-Plus快速入门
10、springboot自定义注解及AOP切面使用
11、使用Shiro实现登陆和权限认证,基于MyBatis
12、集成SpringSecurity实现授权认证
13、SpringBoot集成EasyExcel实现数据导入与导出
14、Spring Task定时任务的实现
15、Quartz快速上手与实践
16、如何用代码实现Spring IOC
17、SpringBoot集成JWT,实现接口的鉴权交互
SpringCloud
Nacos作为服务注册中心
seata1.6.1 结合springcloud实现分布锁的技术笔记
一些技术博客推荐
前端面试相关
看这一篇就够了
java.util包常用的类和接口
CountDownLatch介绍与使用
Common-lang3使用入门
Hutool简单使用入门
lombok 介绍及基本使用方法
git项目统计成员代码行数和提交的次数
mysql 逗号分隔的数据 like查询
使用sonar进行代码质量检查
线上使用jmeter进行压测的时候,使用Arthas诊断工具排查响应慢的接口
php结合phpstudy8、vscode开启xdebug进行代码调试
node-red使用入门
本文档使用 MrDoc 发布
-
+
首页
12、集成SpringSecurity实现授权认证
### 1、引用坐标 ~~~xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> ~~~ 直接启动springboot项目,默认监听8080端口。 查看一下控制台: 打打印一个默认的密码:Using generated security password: e9affc55-75a5-4916-8297-c9d241d56412 访问http://localhost:8080/login ,会打开登录页面,使用用户名:user登录,密码就是刚刚生成的那一长串。  Security自带一个退出的页面,访问 http://localhost:8080/logout  这样就简单的实现了 登录和登出的功能。 当然现实中的系统 不可能 是这么简单的,结合数据库是必不可少的。 ### 2、自定义配置 更强大的功能,需要继承 WebSecurityConfigurerAdapter 实现自己的自定义配置类。 认证的方式有2种:一是账号密码等认证信息写在配置中,二是账号密码等信息从数据库读取。 #### 方式一: ~~~java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 认证配置 * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 设置用户账号信息和权限 auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("111111")) .authorities("addMember", "delMember", "updateMember", "showMember" ); // 如果kaico_admin账户权限的情况 所有的接口都可以访问,如果kaico_add 只能访问addMember auth.inMemoryAuthentication().withUser("test").password(passwordEncoder().encode("111111")) .authorities("addMember"); } /** * 设置密码加密方法 * @return */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * 授权的配置方法 * @param http * @throws Exception */ @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(); // .antMatchers("/**").fullyAuthenticated().and().formLogin(); } } ~~~ 最后一行注意一下,取消使用 security自带的登录页面,使用自己的登录页面,这里使用freemarker模板引擎,相关配置参数见源码。 登录页面 resource/templates/login.ftl 代码: ~~~html <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Login</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> ~~~ 再次访问 http://localhost:8080/login 打开登录页面,使用admin/111111 和 test/111111 分别 登录一下,看看 他们对 addMember delMember 模板的访问权限 有什么不同。 #### 方式二:使用数据库Mysql方式 这种方式的使用也是本文的重点。 毕竟现实中,谁会把自己的用户名和密码写死在配置文件里呢。。 数据库脚本: ~~~sql 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(255) 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', 'admin', '管理员', '$2a$10$pCmsytHGhmqmc.2rokcp9e37eYXEgBwZ8WvNCHL7xwgfCJD1iJ55m', '2022-11-13', '2022-11-13', '1', '1', '1', '1'); INSERT INTO `sys_user` VALUES ('2', 'test', '运维', '$2a$10$pCmsytHGhmqmc.2rokcp9e37eYXEgBwZ8WvNCHL7xwgfCJD1iJ55m', '2022-11-13', '2022-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'); ~~~ 关系图如下,数据库解读: 有两个用户,一个管理员 admin ,一个添加管理员 test,只有添加权限 , admin属于admin角色,admin角色 拥有 增、删、改、查4个接口的权限。 test属于add_admin角色,add_admin角色 仅 拥有 增加 1个接口权限 。  #### 1、快速生成 mapper和实体类。 通过mybatis的代码生成工具,生成 sysUser 、sysPermission、sysRole三张表的mapper和实体类。 #### 2、Model类修改 让SysUser实体类 继承 security 的UserDetails 类,并重写他的所有方法,这里使用了 lombok简化代码。 SysPermission 和 SysRole 保持不变。 SysUser.java ~~~java @Data public class SysUser implements UserDetails{ private Integer id; private String username; private String realname; private String password; private Date createdate; private Date lastlogintime; private Integer enabled; private Integer accountnonexpired; private Integer accountnonlocked; private Integer credentialsnonexpired; // 用户所有权限 private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public boolean isAccountNonExpired() { return this.getAccountnonexpired() == 1; } @Override public boolean isAccountNonLocked() { return this.getAccountnonlocked() == 1; } @Override public boolean isCredentialsNonExpired() { return this.getCredentialsnonexpired() == 1 ; } @Override public boolean isEnabled() { return this.getEnabled() == 1; } } ~~~ #### 3、Mapper修改 修改SysPermissionMapper类,增加一个 findAllPermission 方法。 修改SysUserMapper类,增加findByUsername和findPermissionByUsername两个方法。 **SysPermissionMapper.java** ~~~java @Mapper public interface SysPermissionMapper { @Select(" select * from sys_permission ") List<SysPermission> findAllPermission(); int deleteByPrimaryKey(Integer id); int insert(SysPermission record); int insertSelective(SysPermission record); SysPermission selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(SysPermission record); int updateByPrimaryKey(SysPermission record); } ~~~ **SysUserMapper.java** ~~~java @Mapper public interface SysUserMapper { /** * 根据用户名称查询 * * @param userName * @return */ @Select(" select * from sys_user where username = #{userName}") SysUser 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<SysPermission> findPermissionByUsername(@Param("userName") String userName); int deleteByPrimaryKey(Integer id); int insert(SysUser record); int insertSelective(SysUser record); SysUser selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(SysUser record); int updateByPrimaryKey(SysUser record); } ~~~ #### 4、Service层 增加一个 MyUserDetailsService类,继承自security的UserDetailsService类。并重写方法。 ~~~java @Component @Slf4j public class MyUserDetailsService implements UserDetailsService { @Autowired private SysUserMapper sysUserMapper; /** * loadUserByUserName * * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 1.根据该用户名称查询在数据库中是否存在 SysUser userEntity = sysUserMapper.findByUsername(username); if (userEntity == null) { return null; } // 2.查询对应的用户权限 List<SysPermission> listPermission = sysUserMapper.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; } } ~~~ #### 5、配置config SecurityConfig.java类 完全改了,和之前完全不一样。 ~~~java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired public MyUserDetailsService myUserDetailsService; @Autowired public SysPermissionMapper sysPermissionMapper; /** * 添加授权账户 * * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 设置用户账号信息和权限 auth.userDetailsService(myUserDetailsService); } /** * 设置密码加密方法 * 创建BCryptPasswordEncoder注入容器 * 我们一般会使用Spring Security为我们提供的 BCryptPasswordEncoder * @return */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * 授权的配置方法 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { //配置权限 List<SysPermission> allPermission = sysPermissionMapper.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(); } } ~~~ 至此基本差不多修改完成了。 数据库初始化的时候,使用了111111的加密存储,通地下面的test方法,可以生成自己的密码并替换到数据库。 再次启动服务,访问 http://localhost:8080/login 登录,使用http://localhost:8080/logout 登出。 登录admin和test账号,分别 访问以下地址看看效果吧。 http://localhost:8080/ http://localhost:8080/addMember http://localhost:8080/delMember #### 6、密码加密 **MyTest.java** ~~~java @RunWith(SpringRunner.class) @SpringBootTest public class MyTest { @Test void testBCryptPasswordEncoder() { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); /** * encode():加密方法,传入一个明文,加密后返回一个密文 * 同一明文,每次调用encode()方法生成出来的密文都是不一样的, * 因为内部进行加密的时候,会生成一个【随机的加密盐】, * 底层是通过【加密盐】和原文进行一系列处理之后再进行加密 * 这样的话,虽然明文一样,但是每一次的密文都是不一样的 */ String my_password = passwordEncoder.encode("111111"); System.out.println("my_password = " + my_password); } } ~~~
superadmin
2023年11月3日 17:20
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码