SpringBoot整合SpringSecurity:https://www.bilibili.com/video/BV1KE411i7bC?p=1
SpringBoot整合Shiro框架:https://www.bilibili.com/video/BV1NE411i7S8?p=1
SpringBoot文档:http://felord.cn/_doc/_springboot/2.1.5.RELEASE/_book/
https://docs.spring.io/spring-boot/docs/current/reference/html/
系统安全框架,用于认证、授权
1、SpringSecurity依赖
1 2 3 4 5 | <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>2.5.3</version> </dependency> |
2、命名空间xmlns:th=http://www.thymeleaf.org xmlns:sec=http://www.thymeleaf.org/extras/spring-security xmlns:shiro=http://www.pollix.at/thymeleaf/shiro
3、自己创建SecurityConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | package com.jay.SpringBootStudy8.config; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import static org.springframework.security.config.Customizer.withDefaults; @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { //认证 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // super.configure(auth); //BCryptPasswordEncoder 加密方式,用户名admin、密码123456,可以and()连接多个 auth.inMemoryAuthentication().passwordEncoder( new BCryptPasswordEncoder()) .withUser( "admin" ).password( new BCryptPasswordEncoder().encode( "123456" )).roles( "vip1" ) .and() .withUser( "user1" ).password( new BCryptPasswordEncoder().encode( "123456" )).roles( "vip2" ) ; } //授权 @Override protected void configure(HttpSecurity http) throws Exception { // http.authorizeRequests(authorize -> authorize.anyRequest().authenticated()).oauth2Login(withDefaults()); //首页/所有人可访问 //功能页只有具有权限的人才能访问 http.authorizeRequests() .antMatchers( "/level1" ).hasRole( "vip1" ) .antMatchers( "/level2" ).hasRole( "vip2" ) .antMatchers( "/level3/**" ).hasRole( "vip3" ); //禁用csrf跨站 http.csrf().disable(); //开启登录,定制登录页,自定义请求参数名 和 登录请求地址。 //如果不自定义登录请求地址,那登录form的action需要是loginPage指定的地址 /userLogin http.formLogin().loginPage( "/userLogin" ) .usernameParameter( "uname" ) .passwordParameter( "pwd" ) .loginProcessingUrl( "/loginHandler" ); //记住我,自定义请求参数 http.rememberMe().rememberMeParameter( "remember" ); //注销、删除cookie、清除session http.logout().deleteCookies( "remove" ).invalidateHttpSession( true ); } } |
表单userLogin:
1 2 3 4 5 6 7 8 9 | <form method= "post" th:action= "@{/loginHandler}" > username:<input type= "text" name= "uname" /> <br/> password:<input type= "password" name= "pwd" /> <br/> <input type= "checkbox" name= "remember" />Remember Me <br/> <button type= "submit" >submit</button> </form> |
loginHandler可以处理自己其他的登录逻辑。
页面中获取Spring Security登录用户数据:https://www.cnblogs.com/softidea/p/6677665.html https://blog.csdn.net/cyan20115/article/details/106552758
Spring Security使用jdbc进行权限验证:https://blog.csdn.net/fuzekun/article/details/104344472
Shiro:https://www.jianshu.com/p/7f724bec3dc3
1、引入相关包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.7.1</version> </dependency> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.1.0</version> </dependency> <!--工具类--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.8</version> </dependency> |
2、 自定义ShiroConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | package com.jay.SpringBootStudy8.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @Configuration public class ShiroConfig { //Filter工厂,设置对应的过滤条件和跳转条件 @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier( "securityManager" ) DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(securityManager); /* anon:无需认证即可访问 authc:必须认证才可访问 user:必须拥有记住我的功能,才可以用 perms:必须拥有对某个资源的权限才可以访问 role:拥有某个角色才可以访问 */ Map<String, String> filterMap = new LinkedHashMap<>(); //对所有用户认证,只要登录成功,就可以访问 //filterMap.put("/sys/*", "authc"); //必须拥有资源权限(推荐),users:mgr 是权限码,存于数据库 // filterMap.put("/sys/users", "perms[users:mgr]"); filterMap.put( "/sys/roles" , "perms[roles:mgr]" ); filterMap.put( "/sys/permissions" , "perms[permissions:mgr]" ); filterMap.put( "/sys/products" , "perms[products:mgr]" ); filterMap.put( "/sys/articles" , "perms[articles:mgr]" ); filterMap.put( "/sys/test" , "perms[sys:test]" ); filterMap.put( "/logout" , "logout" ); //登出 filterMap.put( "/loginHandler" , "anon" ); bean.setLoginUrl( "/login" ); //登录 bean.setSuccessUrl( "/sys/main" ); //首页 bean.setUnauthorizedUrl( "/noauth" ); //错误页面,认证不通过跳转 bean.setFilterChainDefinitionMap(filterMap); return bean; } //权限管理,配置主要是Realm的管理认证 @Bean(name = "securityManager" ) public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier( "customRealm" ) CustomRealm customRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(customRealm); return securityManager; } //将自己的验证方式加入容器 @Bean public CustomRealm customRealm() { return new CustomRealm(); } //集成 thymeleaf-extras-shiro,可以在前端使用shiro语法 @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect();} } |
shiro异常处理配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package com.jay.SpringBootStudy8.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import java.util.Properties; @Configuration public class ShiroExceptionConf { @Bean public SimpleMappingExceptionResolver resolver() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties properties = new Properties(); properties.setProperty( "org.apache.shiro.authz.UnauthorizedException" , "/noauth" ); resolver.setExceptionMappings(properties); return resolver; } } |
3、自定义CustomRealm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | package com.jay.SpringBootStudy8.config; import com.jay.SpringBootStudy8.pojo.Permission; import com.jay.SpringBootStudy8.pojo.Role; import com.jay.SpringBootStudy8.pojo.SysUser; import com.jay.SpringBootStudy8.service.PermissionService; import com.jay.SpringBootStudy8.service.RoleService; import com.jay.SpringBootStudy8.service.SysUserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; import java.util.stream.Collectors; public class CustomRealm extends AuthorizingRealm { @Autowired private SysUserService sysUserService; @Autowired private RoleService roleService; @Autowired private PermissionService permissionService; //授权,装配用户权限 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { // Subject subject = SecurityUtils.getSubject(); // Session session = subject.getSession(); // SysUser user = (SysUser) session.getAttribute("user"); //获取登录用户 SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal(); List<Permission> permissions = sysUser.getPermissions(); List<Role> roles = sysUser.getRoles(); //添加角色和权限 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); for (Role role : roles) { //添加角色 info.addRole(role.getName()); } //添加权限 for (Permission perm : permissions) { info.addStringPermission(perm.getCode()); } return info; } //认证,验证用户的账户和密码 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; String username = userToken.getUsername(); SysUser user = sysUserService.getModelByUserName(username); if (user == null ) { return null ; //这里返回后会报出对应异常 } else { List<Role> roles = roleService.getAllByUserId(user.getId()); user.setRoles(roles); List<Integer> roleIds = roles.stream().map(Role::getId).collect(Collectors.toList()); List<Permission> perms = permissionService.getAllByRoleIds(roleIds); user.setPermissions(perms); //盐值加密验证密码 String realmName = getName(); ByteSource credentialsSalt = ByteSource.Util.bytes(username); //这里的参数要给个唯一的; SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPwd(), credentialsSalt, realmName); // 存入Shiro的Session,这里不是HttpSession,是Shiro独立的Session, // 只能通过SecurityUtils.getSubject().getSession()获取 Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession(); session.setAttribute( "user" , user); return info; } } } |
4、登录Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | package com.jay.SpringBootStudy8.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ByteSource; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; //在templates目录下的页面只能通过controller来跳转 @Controller public class IndexController { @RequestMapping( "/noauth" ) public String unauthorized() { return "unauthorized" ; } @RequestMapping({ "/" , "/login" }) public String login() { return "login" ; } @RequestMapping( "/logout" ) public String logout() { Subject subject = SecurityUtils.getSubject(); subject.logout(); return "login" ; } @PostMapping( "/loginHandler" ) public String loginHandler(String name, String pwd, Model model) { //MD5加密 ByteSource credentialsSalt = ByteSource.Util.bytes(name); Object obj = new SimpleHash( "MD5" , pwd, credentialsSalt, 1); String pwd2 = obj.toString(); //获取当前用户 Subject subject = SecurityUtils.getSubject(); //封装登录数据 UsernamePasswordToken token = new UsernamePasswordToken(name, pwd2); try { //执行登录方法 subject.login(token); return "redirect:/sys/main" ; } catch (UnknownAccountException e) { model.addAttribute( "msg" , "用户名不存在" ); return "login" ; } catch (IncorrectCredentialsException e) { model.addAttribute( "msg" , "密码错误" ); return "login" ; } } } |
5、 数据库中权限表:permissions
1 2 3 4 5 6 7 | Id Name Code -------------------------------------- 1 用户管理 users:mgr 2 角色管理 roles:mgr 3 权限管理 permissions:mgr 4 商品管理 products:mgr 5 图文管理 articles:mgr |
6、前端需要整合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <div th:fragment= "header" > <a th:href= "@{/sys/main}" >主页</a> <span shiro:hasPermission= "users:mgr" > <a th:href= "@{/sys/users}" >用户管理</a> </span> <span shiro:hasPermission= "roles:mgr" > <a th:href= "@{/sys/roles}" >角色管理</a> </span> <span shiro:hasPermission= "permissions:mgr" > <a th:href= "@{/sys/permissions}" >权限管理</a> </span> <span shiro:hasPermission= "products:mgr" > <a th:href= "@{/sys/products}" >商品管理</a> </span> <span shiro:hasPermission= "articles:mgr" > <a th:href= "@{/sys/articles}" >图文管理</a> </span> <span> <a th:href= "@{/logout}" >注销</a> </span> </div> |
引入fragment:<div th:insert="~{common/head :: header}"></div>,shiro前端提示需要引入html命名空间:xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
springboot+shiro实现自定义密码加密及验证(Bcrypt):https://blog.csdn.net/qq_21537671/article/details/107280447
shiro注解:https://blog.csdn.net/qi923701/article/details/75224554/
shiro thymeleaf:https://blog.csdn.net/qq_34579313/article/details/82024058
swagger引用包
1 2 3 4 5 | <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.5.10</version> </dependency> |
访问:http://localhost:8091/swagger-ui.html就可以看到所有接口,具体使用方法百度。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】