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");
這時候會跳轉到登錄頁面
先注冊一個用戶
查看數據庫
這時候在登錄就可以訪問到首頁了
很簡單的一個用戶認證功能,下面我們繼續完善