從訪問開始
http://localhost:8080
@GetMapping(value = "") public String index() { LOGGER.info("這事空地址在請求路徑"); Subject s = SecurityUtils.getSubject(); return s.isAuthenticated() ? "redirect:index" : "login"; }
登錄以后結果就是true ,否則去登錄
<input name="username" placeholder="用戶名"> <input name="password" placeholder="密碼" type="password">
<input type="checkbox" name="rememberMe" value="true" lay-skin="primary" checked title="記住帳號?">
<input name="code" placeholder="驗證碼" type="text" > <img src="${base}/genCaptcha" width="116" height="36" id="mycode">
圖片驗證碼
{base} 來源於攔截器
生產驗證碼
/** * 獲取驗證碼圖片和文本(驗證碼文本會保存在HttpSession中) */ @GetMapping("/genCaptcha") public void genCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException { //設置頁面不緩存 response.setHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); String verifyCode = VerifyCodeUtil.generateTextCode(VerifyCodeUtil.TYPE_ALL_MIXED, 4, null); //將驗證碼放到HttpSession里面 request.getSession().setAttribute(Constants.VALIDATE_CODE, verifyCode); LOGGER.info("本次生成的驗證碼為[" + verifyCode + "],已存放到HttpSession中"); //設置輸出的內容的類型為JPEG圖像 response.setContentType("image/jpeg"); BufferedImage bufferedImage = VerifyCodeUtil.generateImageCode(verifyCode, 116, 36, 5, true, new Color(249,205,173), null, null); //寫給瀏覽器 ImageIO.write(bufferedImage, "JPEG", response.getOutputStream()); }
登錄請求:
<form action="${base}/login/main" method="post">
<button class="layui-btn login_btn" lay-submit="" lay-filter="login">登錄</button>
@PostMapping("login/main") @ResponseBody @SysLog("用戶登錄") public RestResponse loginMain(HttpServletRequest request) { String username = request.getParameter("username"); String password = request.getParameter("password"); String rememberMe = request.getParameter("rememberMe"); String code = request.getParameter("code"); if(StringUtils.isBlank(username) || StringUtils.isBlank(password)){ return RestResponse.failure("用戶名或者密碼不能為空"); } if(StringUtils.isBlank(rememberMe)){ return RestResponse.failure("記住我不能為空"); } if(StringUtils.isBlank(code)){ return RestResponse.failure("驗證碼不能為空"); } Map<String,Object> map = Maps.newHashMap(); String error = null; HttpSession session = request.getSession(); if(session == null){ return RestResponse.failure("session超時"); } String trueCode = (String)session.getAttribute(Constants.VALIDATE_CODE); if(StringUtils.isBlank(trueCode)){ return RestResponse.failure("驗證碼超時"); } if(StringUtils.isBlank(code) || !trueCode.toLowerCase().equals(code.toLowerCase())){ error = "驗證碼錯誤"; }else { /*就是代表當前的用戶。*/ Subject user = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username,password,Boolean.valueOf(rememberMe)); try { user.login(token); if (user.isAuthenticated()) { map.put("url","index"); } }catch (IncorrectCredentialsException e) { error = "登錄密碼錯誤."; } catch (ExcessiveAttemptsException e) { error = "登錄失敗次數過多"; } catch (LockedAccountException e) { error = "帳號已被鎖定."; } catch (DisabledAccountException e) { error = "帳號已被禁用."; } catch (ExpiredCredentialsException e) { error = "帳號已過期."; } catch (UnknownAccountException e) { error = "帳號不存在"; } catch (UnauthorizedException e) { error = "您沒有得到相應的授權!"; } } if(StringUtils.isBlank(error)){ return RestResponse.success("登錄成功").setData(map); }else{ return RestResponse.failure(error); } }
登出
@SysLog("退出系統") public String logOut(){ SecurityUtils.getSubject().logout(); return "redirect:/login"; }
String error = null; user.login(token); if (user.isAuthenticated()) { map.put("url","index"); } if(StringUtils.isBlank(error)){ return RestResponse.success("登錄成功").setData(map); }else{ return RestResponse.failure(error); }
如果登錄成功會跳轉到index.htlm
否則返回錯誤信息
看一下bean RestResponse
public class RestResponse extends HashMap<String, Object> { public static RestResponse success(){ return success("成功"); } public static RestResponse success(String message){ RestResponse restResponse = new RestResponse(); restResponse.setSuccess(true); restResponse.setMessage(message); return restResponse; } public static RestResponse failure(String message){ RestResponse restResponse = new RestResponse(); restResponse.setSuccess(false); restResponse.setMessage(message); return restResponse; } public RestResponse setSuccess(Boolean success) { if (success != null) put("success", success); return this; } public RestResponse setMessage(String message) { if (message != null) put("message", message); return this; } public RestResponse setData(Object data) { if (data != null) put("data", data); return this; }
二、shiro 配置
2.1 shiro - rdis 配置
@Configuration public class ShiroConfig { private Logger logger = LoggerFactory.getLogger(ShiroConfig.class); @Value("${spring.redis.host}") private String jedisHost; @Value("${spring.redis.port}") private Integer jedisPort; @Value("${spring.redis.password}") private String jedisPassword; @Bean public FilterRegistrationBean delegatingFilterProxy(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); DelegatingFilterProxy proxy = new DelegatingFilterProxy(); proxy.setTargetFilterLifecycle(true); proxy.setTargetBeanName("shiroFilter"); filterRegistrationBean.setFilter(proxy); filterRegistrationBean.setDispatcherTypes(DispatcherType.ERROR,DispatcherType.REQUEST,DispatcherType.FORWARD,DispatcherType.INCLUDE); return filterRegistrationBean; } @Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("authRealm")AuthRealm authRealm){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(securityManager(authRealm)); bean.setSuccessUrl("/index"); bean.setLoginUrl("/login"); Map<String,Filter> map = Maps.newHashMap(); map.put("authc",new CaptchaFormAuthenticationFilter()); bean.setFilters(map); //配置訪問權限 LinkedHashMap<String, String> filterChainDefinitionMap = Maps.newLinkedHashMap(); filterChainDefinitionMap.put("/static/**","anon"); filterChainDefinitionMap.put("/showBlog/**","anon"); filterChainDefinitionMap.put("/blog/**","anon"); filterChainDefinitionMap.put("/login/main","anon"); filterChainDefinitionMap.put("/genCaptcha","anon"); filterChainDefinitionMap.put("/systemLogout","authc"); filterChainDefinitionMap.put("/**","authc"); bean.setFilterChainDefinitionMap(filterChainDefinitionMap); return bean; } @Bean public SecurityManager securityManager(@Qualifier("authRealm")AuthRealm authRealm){ logger.info("- - - - - - -shiro開始加載- - - - - - "); DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); defaultWebSecurityManager.setRealm(authRealm); defaultWebSecurityManager.setRememberMeManager(rememberMeManager()); defaultWebSecurityManager.setSessionManager(webSessionManager()); defaultWebSecurityManager.setCacheManager(cacheManager()); return defaultWebSecurityManager; } @Bean public SimpleCookie rememberMeCookie(){ //這個參數是cookie的名稱,對應前端的checkbox的name = rememberMe SimpleCookie cookie = new SimpleCookie("rememberMe"); cookie.setHttpOnly(true); //記住我有效期長達30天 cookie.setMaxAge(2592000); return cookie; } @Bean public CookieRememberMeManager rememberMeManager(){ CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); rememberMeManager.setCookie(rememberMeCookie()); rememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag==")); return rememberMeManager; } /** * AOP式方法級權限檢查 * @return */ @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); return creator; } /** * 保證實現了Shiro內部lifecycle函數的bean執行 * @return */ @Bean public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor(); } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("authRealm")AuthRealm authRealm) { SecurityManager manager= securityManager(authRealm); AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(manager); return advisor; } @Bean public SessionManager webSessionManager(){ DefaultWebSessionManager manager = new DefaultWebSessionManager(); //設置session過期時間為1小時(單位:毫秒),默認為30分鍾 manager.setGlobalSessionTimeout(60 * 60 * 1000); manager.setSessionValidationSchedulerEnabled(true); manager.setSessionDAO(redisSessionDAO()); return manager; } @Bean public RedisManager redisManager(){ RedisManager manager = new RedisManager(); manager.setHost(jedisHost); manager.setPort(jedisPort); //這里是用戶session的時長 跟上面的setGlobalSessionTimeout 應該保持一直(上面是1個小時 下面是秒做單位的 我們設置成3600) manager.setExpire(60 * 60); manager.setPassword(jedisPassword); return manager; } @Bean public RedisSessionDAO redisSessionDAO(){ RedisSessionDAO sessionDAO = new RedisSessionDAO(); sessionDAO.setKeyPrefix("wl_"); sessionDAO.setRedisManager(redisManager()); return sessionDAO; } @Bean("myCacheManager") public RedisCacheManager cacheManager(){ RedisCacheManager manager = new RedisCacheManager(); manager.setRedisManager(redisManager()); return manager; } }
啟用redis 緩存
@EnableCaching @Configuration public class RedisCacheConfig { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory) { StringRedisTemplate redisTemplate = new StringRedisTemplate(factory); // JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(); //這里如果啟用fastjson序列化對象到redis的話 啟動必須加參數 -Dfastjson.parser.autoTypeSupport=true // RedisSerializer fastJson = fastJson2JsonRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) { return new RedisCacheManager(redisTemplate); } }
認證和授權
@Component(value = "authRealm") public class AuthRealm extends AuthorizingRealm { @Autowired @Lazy private UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { ShiroUser shiroUser = (ShiroUser)principalCollection.getPrimaryPrincipal(); User user = userService.findUserByLoginName(shiroUser.getloginName()); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); Set<Role> roles = user.getRoleLists(); Set<String> roleNames = Sets.newHashSet(); for (Role role : roles) { if(StringUtils.isNotBlank(role.getName())){ roleNames.add(role.getName()); } } Set<Menu> menus = user.getMenus(); Set<String> permissions = Sets.newHashSet(); for (Menu menu : menus) { if(StringUtils.isNotBlank(menu.getPermission())){ permissions.add(menu.getPermission()); } } info.setRoles(roleNames); info.setStringPermissions(permissions); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String username = (String)token.getPrincipal(); User user = userService.findUserByLoginName(username); if(user == null) { throw new UnknownAccountException();//沒找到帳號 } if(Boolean.TRUE.equals(user.getLocked())) { throw new LockedAccountException(); //帳號鎖定 } byte[] salt = Encodes.decodeHex(user.getSalt()); SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( new ShiroUser(user.getId(),user.getLoginName(),user.getNickName(), user.getIcon()), user.getPassword(), //密碼 ByteSource.Util.bytes(salt), getName() //realm name ); return authenticationInfo; }
密碼處理
/** * Hex編碼. */ public static String encodeHex(byte[] input) { return new String(Hex.encodeHex(input)); } /** * Hex解碼. */ public static byte[] decodeHex(String input) { try { return Hex.decodeHex(input.toCharArray()); } catch (DecoderException e) { throw Exceptions.unchecked(e); } }