(二) shiro集成 --《springboot与shiro整合》
2.1 引入shiro相关依赖
<dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.2</version> </dependency>
2.2 自定义Realm
@Component public class UserRealm extends AuthorizingRealm{ @Autowired private UserService userService; /** * 授权 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); //在数据库中查询用户拥有的角色/权限 authorizationInfo.setRoles(userService.findRoles(username)); authorizationInfo.setStringPermissions(userService.findPermissions(username)); return authorizationInfo; } /** * 验证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); User user = userService.findByUsername(username); if(user == null){ throw new UnknownAccountException(); //没找到账号 } if(Boolean.TRUE.equals(user.getLocked())){ throw new LockedAccountException(); //账号被锁定 } SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getCredentialsSalt()), //salt = username+salt getName()); return authenticationInfo; } }
2.3 ShiroConfig
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //拦截器. Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); //配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了 filterChainDefinitionMap.put("logout", "logout"); filterChainDefinitionMap.put("/user/login", "anon"); // authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问 filterChainDefinitionMap.put("/user/**", "anon");
filterChainDefinitionMap.put("/test/**", "authc");
filterChainDefinitionMap.put("/page/**", "authc"); // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login.html");
shiroFilterFactoryBean.setUnauthorizedUrl("/page/fail.html");//未授权跳转 //登录成功跳转的链接 (这个不知道怎么用,我都是自己实现跳转的) shiroFilterFactoryBean.setSuccessUrl("/page/main.html"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 凭证匹配器 * 由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法; hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5("")); return hashedCredentialsMatcher; } @Bean public UserRealm myShiroRealm() { UserRealm myShiroRealm = new UserRealm();
//使用加密
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); return securityManager; } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor(); } /** * 注册全局异常处理 * @return */ @Bean(name = "exceptionHandler") public HandlerExceptionResolver handlerExceptionResolver() { return new ExceptionHandler(); } }
2.4 创建UserController
@RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @RequestMapping("/login") public ModelAndView login(User loginUser,ServletRequest request){ ModelAndView view = new ModelAndView(); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(loginUser.getUsername(),loginUser.getPassword()); if(!subject.isAuthenticated()){ subject.login(token); } //获取上一次请求路径 SavedRequest savedRequest = WebUtils.getSavedRequest(request); String url = ""; if(savedRequest != null){ url = savedRequest.getRequestUrl(); }else{ url = "/page/main.html"; } view.setViewName("redirect:"+url); return view; } @RequestMapping("/register") public ModelAndView add(User user){ ModelAndView view = new ModelAndView(); userService.createUser(user); view.setViewName("redirect:/login.html"); return view; } @RequestMapping("/logout") public String logout(User loginUser){ Subject subject = SecurityUtils.getSubject(); subject.logout(); return "已注销"; } }
UserService
@Override public Long createUser(User user) { PasswordHelper.encryptPassword(user); return userDao.createUser(user); }
PasswordHelper (加密,保存到数据库的时候使用)
private static RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator(); //这些要与Realm中的一致 private static String algorithmName = "md5"; private final static int hashIterations = 2; static public void encryptPassword(User user) { //加盐 user.setSalt(randomNumberGenerator.nextBytes().toHex()); String newPassword = new SimpleHash(algorithmName, user.getPassword(), ByteSource.Util.bytes(user.getCredentialsSalt()), hashIterations).toHex(); user.setPassword(newPassword); }
下面我们来测试一下吧 (页面代码这里就不写了)
我们先访问 http://localhost:8080/page/main.html 由于在ShiroConfig中设置了 page 目录下面的所有文件都需要认真通过才能访问
filterChainDefinitionMap.put("/page/**", "authc");
这时候会跳转到登录页面
先注册一个用户
查看数据库
这时候在登录就可以访问到首页了
很简单的一个用户认证功能,下面我们继续完善