springboot和shiro的整合
直接贴上代码
1. 所需要的jar包
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.4.0</version> </dependency>
2. 所需要的Configuration
/** * Shiro的配置文件 */ @Configuration public class ShiroConfig { /** * 自定义Realm,可以多个 */ @Bean public ShiroRealm myRealm(){ ShiroRealm shiroRealm = new ShiroRealm(); return shiroRealm; } @Bean public SessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); // 设置session过期时间为30分钟 sessionManager.setGlobalSessionTimeout(30 * 60 * 1000); // 设置定时器,检查session时候国企 sessionManager.setSessionValidationSchedulerEnabled(true); // 去掉shiro登录时url里的JSESSIONID sessionManager.setSessionIdUrlRewritingEnabled(false); return sessionManager; } /** * SecurityManager 安全管理器;Shiro的核心 */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); //将session设置到SecurityManager中 manager.setSessionManager(sessionManager()); //将realm设置到SecurityManager中 manager.setRealm(myRealm()); return manager; } /** * shiroFilter过滤类 * @return */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(securityManager()); bean.setLoginUrl("/"); // 登录的路径 bean.setSuccessUrl("/success"); // 登录成功后跳转的路径 bean.setUnauthorizedUrl("/403"); // 验证失败后跳转的路径 Map<String, String> map = new LinkedHashMap<>(); map.put("/captcha", "anon"); map.put("/captcha.jpg", "anon"); map.put("/statics/**", "anon"); map.put("/login.html", "anon"); map.put("/favicon.ico", "anon"); map.put("/login", "anon"); map.put("/**", "authc"); map.put("/logout", "logout"); bean.setFilterChainDefinitionMap(map); return bean; } /** * 配置Shiro生命周期处理器 */ @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * 自动创建代理类,若不添加,Shiro的注解可能不会生效。 */ @Bean @DependsOn({"lifecycleBeanPostProcessor"}) public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } /** * 开启Shiro的注解 */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; } /** * 配置加密匹配,使用MD5的方式,进行1024次加密 */ // @Bean // public HashedCredentialsMatcher hashedCredentialsMatcher() { // HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); // hashedCredentialsMatcher.setHashAlgorithmName("des"); // hashedCredentialsMatcher.setHashIterations(1024); // return hashedCredentialsMatcher; // } }
3.重写realm
public class ShiroRealm extends AuthorizingRealm { @Autowired private LoginDao loginDao; // 验证权限(登录的时候使用的) @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String username = token.getUsername(); UserDO userDO = loginDao.selectOne(new QueryWrapper<UserDO>().eq("username", username)); if (userDO == null) { throw new UnknownAccountException("未知账号");//没找到帐号 } // 交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配 //使用 ByteSource.Util.bytes() 来计算盐值. return new SimpleAuthenticationInfo(userDO, userDO.getPassword(),ByteSource.Util.bytes(userDO.getPassword()), getName()); } // 添加权限 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } /** * 设定Password校验 */ @PostConstruct public void initCredentialsMatcher() { //重写shiro的密码验证,让shiro用我自己的验证 setCredentialsMatcher(new CustomCredentialsMatcher()); } }
4. 重写密码验证类
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher { /** * 此方法为HashedCredentialsMatcher类中真正验证密码是否一致的地方 */ @Override public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) { // 明明authcToken跟info两个对象的里的Credentials类型都是Object,断点看到的类型都是 char[] // 但是 token里转成String要先强转成 char[] // 而info里取Credentials就可以直接使用 String.valueOf() 转成String UsernamePasswordToken token = (UsernamePasswordToken) authcToken; Object tokenCredentials = encrypt(String.valueOf(token.getPassword())); Object infocredentials = getCredentials(info); return equals(tokenCredentials, infocredentials); } /** * 明文密码加密,将传进来密码加密方法 */ public String encrypt(String password) { //这里可以选择自己的密码验证方式 比如 md5或者sha256等 String passwordEncrypt = password return passwordEncrypt; } }
5. 登录的Controller
public userDO login(@RequestBody LoginRequest loginRequest) { try { Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(loginRequest.getUsername(), loginRequest.getPassword()); subject.login(token); System.out.println("登录成功"); } catch (UnknownAccountException e) { return HttpResultUtil.error("账号不存在"); } catch (IncorrectCredentialsException e) { return HttpResultUtil.error("密码错误"); } catch (AuthenticationException e) { return HttpResultUtil.error("账户被禁用,无法登录,请联系管理员"); } UserDO userDO = (UserDO) SecurityUtils.getSubject().getPrincipal(); return userDO }