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依赖
<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
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:
<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、引入相关包
<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
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异常处理配置:
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
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
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
Id Name Code -------------------------------------- 1 用户管理 users:mgr 2 角色管理 roles:mgr 3 权限管理 permissions:mgr 4 商品管理 products:mgr 5 图文管理 articles:mgr
6、前端需要整合
<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引用包
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.5.10</version> </dependency>
访问:http://localhost:8091/swagger-ui.html就可以看到所有接口,具体使用方法百度。