SpringBoot集成篇(一)无状态shiro
来源:互联网 发布:社交软件英语 编辑:程序博客网 时间:2024/05/21 10:36
springboot是现如今很流行的微服务框架
鉴权方面内置了spring自家的spring security ,比较方便,这里阐述用springboot集成另一大身份验证和授权框架 shiro。
网上也有很多boot集成shiro的实例 但是都不太完整,且不是stateless无状态的,不适用于现在这种前后端分离格局。
这里特此记录下辛酸的集成过程,让大家少走一点弯路。
shiro的集成方式为无状态(禁用session),通过每次请求带上token进行鉴权。
为了演示 token禁用简单的uuid32生成。
jdk版本1.7 springboot版本1.5.3
springboot环境搭建这里不在说明,直入正题。
依赖
1.引入shiro的maven依赖,这里用最新的1.3.2版本
<!-- shiro --><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.2</version></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.3.2</version></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version></dependency>
代码实现
1.ShiroConfig配置
package com.lhy.config;import java.util.HashMap;import java.util.LinkedHashMap;import java.util.Map;import javax.servlet.Filter;import net.sf.ehcache.CacheManager;import org.apache.shiro.SecurityUtils;import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;import org.apache.shiro.mgt.DefaultSubjectDAO;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.session.mgt.DefaultSessionManager;import org.apache.shiro.spring.LifecycleBeanPostProcessor;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.filter.authc.AnonymousFilter;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import com.lhy.auth.service.MyAuthService;import com.lhy.common.shiro.filter.StatelessAuthcFilter;import com.lhy.common.shiro.realm.StatelessRealm;import com.lhy.common.shiro.service.PrincipalService;import com.lhy.common.shiro.subject.StatelessDefaultSubjectFactory;import com.lhy.common.shiro.token.helper.EhCacheUserTokenHelper;import com.lhy.common.shiro.token.manager.TokenManager;import com.lhy.common.shiro.token.manager.impl.DefaultTokenManagerImpl;/** * shiro配置 * @author luanhy * */@Configurationpublic class ShiroConfig { private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class); /** * token管理类 * @param cacheManager * @param bootProperties * @return */ @Bean public TokenManager tokenManager(CacheManager cacheManager,BootProperties bootProperties){ logger.info("ShiroConfig.getTokenManager()"); //默认的token管理实现类 32位uuid DefaultTokenManagerImpl tokenManager = new DefaultTokenManagerImpl(); //token失效时间 tokenManager.setExpirateTime(bootProperties.getExpirateTime()); //用户token委托给ehcache管理 EhCacheUserTokenHelper ehCacheUserTokenHelper = new EhCacheUserTokenHelper(); ehCacheUserTokenHelper.setCacheManager(cacheManager); tokenManager.setUserTokenOperHelper(ehCacheUserTokenHelper); // 安全的jwttoken方式 不用担心token被拦截 // RaFilterJwtTokenManagerImpl tokenManager1 = new RaFilterJwtTokenManagerImpl();// JwtUtil jwtUtil = new JwtUtil();// jwtUtil.setProfiles(bootProperties.getKey());// tokenManager1.setJwtUtil(jwtUtil);// tokenManager1.setExpirateTime(bootProperties.getExpirateTime());// EhCacheLoginFlagHelper ehCacheLoginFlagHelper = new EhCacheLoginFlagHelper();// ehCacheLoginFlagHelper.setCacheManager(cacheManager);// tokenManager1.setLoginFlagOperHelper(ehCacheLoginFlagHelper);// tokenManager1.setUserTokenOperHelper(ehCacheUserTokenHelper); return tokenManager; } /** * 无状态域 * @param tokenManager * @param principalService 登陆账号服务需要实现PrincipalService接口 * @param authorizationService 授权服务 需要实现authorizationService接口 * @return */ @Bean public StatelessRealm statelessRealm(TokenManager tokenManager,@Qualifier("userService") PrincipalService principalService,MyAuthService authorizationService){ logger.info("ShiroConfig.getStatelessRealm()"); StatelessRealm realm = new StatelessRealm(); realm.setTokenManager(tokenManager); realm.setPrincipalService(principalService); realm.setAuthorizationService(authorizationService); return realm; } /** * 会话管理类 禁用session * @return */ @Bean public DefaultSessionManager defaultSessionManager(){ logger.info("ShiroConfig.getDefaultSessionManager()"); DefaultSessionManager manager = new DefaultSessionManager(); manager.setSessionValidationSchedulerEnabled(false); return manager; } /** * 安全管理类 * @param statelessRealm * @return */ @Bean public DefaultWebSecurityManager defaultWebSecurityManager(StatelessRealm statelessRealm){ logger.info("ShiroConfig.getDefaultWebSecurityManager()"); DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); //禁用sessionStorage DefaultSubjectDAO de = (DefaultSubjectDAO)manager.getSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator =(DefaultSessionStorageEvaluator)de.getSessionStorageEvaluator(); defaultSessionStorageEvaluator.setSessionStorageEnabled(false); manager.setRealm(statelessRealm); //无状态主题工程,禁止创建session StatelessDefaultSubjectFactory statelessDefaultSubjectFactory = new StatelessDefaultSubjectFactory(); manager.setSubjectFactory(statelessDefaultSubjectFactory); manager.setSessionManager(defaultSessionManager()); //设置了SecurityManager采用使用SecurityUtils的静态方法 获取用户等 SecurityUtils.setSecurityManager(manager); return manager; } /** * Shiro生命周期处理 * @return */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ logger.info("ShiroConfig.getLifecycleBeanPostProcessor()"); return new LifecycleBeanPostProcessor(); } /** * 身份验证过滤器 * @param manager * @param tokenManager * @return */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager manager,TokenManager tokenManager){ logger.info("ShiroConfig.getShiroFilterFactoryBean()"); ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(manager); Map<String,Filter> filters = new HashMap<String,Filter>(); //无需增加 shiro默认会添加该filter //filters.put("anon", anonymousFilter()); //无状态授权过滤器 //特别注意!自定义的StatelessAuthcFilter //不能声明为bean 否则shiro无法管理该filter生命周期,该过滤器会执行其他过滤器拦截过的路径 //这种情况通过普通spring项目集成shiro不会出现,boot集成会出现,搞了好久才明白,巨坑 StatelessAuthcFilter statelessAuthcFilter = statelessAuthcFilter(tokenManager); filters.put("statelessAuthc", statelessAuthcFilter); bean.setFilters(filters); //注意是LinkedHashMap 保证有序 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); //1, 相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个)。 //2, 两个url规则都可以匹配同一个url,只执行第一个 filterChainDefinitionMap.put("/html/**", "anon"); filterChainDefinitionMap.put("/resource/**", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/login/**", "anon"); filterChainDefinitionMap.put("/favicon.ico", "anon"); filterChainDefinitionMap.put("/**", "statelessAuthc"); bean.setFilterChainDefinitionMap(filterChainDefinitionMap); //字符串方式创建过滤链 \n换行 // String s = "/resource/**=anon\n/html/**=anon\n/login/**=anon\n/login=anon\n/**=statelessAuthc";// bean.setFilterChainDefinitions(s); return bean; } /** * * @Function: ShiroConfig::anonymousFilter * @Description: 该过滤器无需增加 shiro默认会添加该filter * @return * @version: v1.0.0 * @author: hyluan * @date: 2017年5月8日 下午5:39:10 * * Modification History: * Date Author Version Description *------------------------------------------------------------- */ public AnonymousFilter anonymousFilter(){ logger.info("ShiroConfig.anonymousFilter()"); return new AnonymousFilter(); } /** * * @Function: ShiroConfig::statelessAuthcFilter * @Description: 无状态授权过滤器 注意不能声明为bean 否则shiro无法管理该filter生命周期,<br> * 该过滤器会执行其他过滤器拦截过的路径 * @param tokenManager * @return * @version: v1.0.0 * @author: hyluan * @date: 2017年5月8日 下午5:38:55 * * Modification History: * Date Author Version Description *------------------------------------------------------------- */ public StatelessAuthcFilter statelessAuthcFilter(TokenManager tokenManager){ logger.info("ShiroConfig.statelessAuthcFilter()"); StatelessAuthcFilter statelessAuthcFilter = new StatelessAuthcFilter(); statelessAuthcFilter.setTokenManager(tokenManager); return statelessAuthcFilter; } }
package com.lhy.config;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;@Component@ConfigurationProperties(prefix = "shiro.token") public class BootProperties {private String key; private long expirateTime;public String getKey() {return key;}public void setKey(String key) {this.key = key;}public long getExpirateTime() {return expirateTime;}public void setExpirateTime(long expirateTime) {this.expirateTime = expirateTime;} }
application.properties
shiro.token.key=helloworldshiro.token.expirateTime=900
3.StatelessAuthcFilter 自定义权限过滤器
package com.lhy.common.shiro.filter;import java.io.IOException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.log4j.Logger;import org.apache.shiro.web.filter.AccessControlFilter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.util.StringUtils;import com.lhy.common.shiro.token.StatelessToken;import com.lhy.common.shiro.token.manager.TokenManager;import com.lhy.common.shiro.util.RequestUtil;/** * 无状态授权过滤器 * @author luanhy * */public class StatelessAuthcFilter extends AccessControlFilter {private final Logger logger = Logger.getLogger(StatelessAuthcFilter.class);@Autowiredprivate TokenManager tokenManager;public TokenManager getTokenManager() {return tokenManager;}public void setTokenManager(TokenManager tokenManager) {this.tokenManager = tokenManager;}@Overrideprotected boolean isAccessAllowed(ServletRequest request,ServletResponse response, Object mappedValue) throws Exception {HttpServletRequest httpRequest = (HttpServletRequest) request;logger.info("拦截到的url:" + httpRequest.getRequestURL().toString());// 前段token授权信息放在请求头中传入String authorization = RequestUtil.newInstance().getRequestHeader((HttpServletRequest) request, "authorization");if (StringUtils.isEmpty(authorization)) {onLoginFail(response, "请求头不包含认证信息authorization");return false;}// 获取无状态TokenStatelessToken accessToken = tokenManager.getToken(authorization);try {// 委托给Realm进行登录getSubject(request, response).login(accessToken);} catch (Exception e) {logger.error("auth error:" + e.getMessage(), e);onLoginFail(response, "auth error:" + e.getMessage()); // 6、登录失败return false;}// 通过isPermitted 才能调用doGetAuthorizationInfo方法获取权限信息getSubject(request, response).isPermitted(httpRequest.getRequestURI());return true;}@Overrideprotected boolean onAccessDenied(ServletRequest request,ServletResponse response) throws Exception {return false;} //登录失败时默认返回401状态码 private void onLoginFail(ServletResponse response,String errorMsg) throws IOException { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.setContentType("text/html"); httpResponse.setCharacterEncoding("utf-8"); httpResponse.getWriter().write(errorMsg); httpResponse.getWriter().close(); } }
3.StatelessRealm无状态域
package com.lhy.common.shiro.realm;import java.util.List;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authc.UnknownAccountException;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.authz.SimpleAuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import com.lhy.common.shiro.service.AuthorizationService;import com.lhy.common.shiro.service.PrincipalService;import com.lhy.common.shiro.token.StatelessToken;import com.lhy.common.shiro.token.manager.TokenManager;public class StatelessRealm extends AuthorizingRealm {private TokenManager tokenManager;@SuppressWarnings("rawtypes")private PrincipalService principalService;@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof StatelessToken;}private AuthorizationService authorizationService;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //根据用户名查找角色,请根据需求实现 String userCode = (String) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();List<String> selectRoles = authorizationService.selectRoles(userCode);authorizationInfo.addRoles(selectRoles);return authorizationInfo;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {StatelessToken statelessToken = (StatelessToken)token;String userCode = (String)statelessToken.getPrincipal();checkUserExists(userCode);String credentials = (String)statelessToken.getCredentials();boolean checkToken = tokenManager.checkToken(statelessToken);if (checkToken) {return new SimpleAuthenticationInfo(userCode, credentials, super.getName());}else{throw new AuthenticationException("token认证失败");}}private void checkUserExists(String userCode) throws AuthenticationException {Object principal = principalService.select(userCode);if(principal == null){throw new UnknownAccountException("userCode "+userCode+" wasn't in the system");}}public TokenManager getTokenManager() {return tokenManager;}public void setTokenManager(TokenManager tokenManager) {this.tokenManager = tokenManager;}@SuppressWarnings("rawtypes")public PrincipalService getPrincipalService() {return principalService;}@SuppressWarnings("rawtypes")public void setPrincipalService(PrincipalService principalService) {this.principalService = principalService;}public AuthorizationService getAuthorizationService() {return authorizationService;}public void setAuthorizationService(AuthorizationService authorizationService) {this.authorizationService = authorizationService;}}
3.PrincipalService用户服务
package com.lhy.common.shiro.service;/** * 用户服务 * @author luanhy * * @param <T> */public interface PrincipalService<T> {/** * 根据用户id获取用户信息 * @param principal * @return */T select(String principal);}
4.StatelessDefaultSubjectFactory无状态主题工厂
package com.lhy.common.shiro.subject;import org.apache.shiro.subject.Subject;import org.apache.shiro.subject.SubjectContext;import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;/** * 无状态主题工厂 * @author luanhy * */public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {@Overridepublic Subject createSubject(SubjectContext context) {//不创建session context.setSessionCreationEnabled(false);return super.createSubject(context);}}
package com.lhy.common.shiro.token.helper;/** * 用户令牌操作接口 * @author luanhy * */public interface UserTokenOperHelper {/** * 根据用户编码获取令牌 * @param userCode * @return */public String getUserToken(String userCode);/** * 更新令牌, 每次获取令牌成功时更新令牌失效时间 * @param userCode * @param token * @param seconds */public void updateUserToken(String userCode,String token,long seconds);/** * 删除令牌 * @param userCode */public void deleteUserToken(String userCode);}
6.ehcache用户令牌帮助类 ,其他缓存同样支持,实现UserTokenOperHelper接口,编码各自存取token逻辑即可
package com.lhy.common.shiro.token.helper;import java.util.List;import net.sf.ehcache.Cache;import net.sf.ehcache.CacheManager;import net.sf.ehcache.Element;/** * ehcache用户令牌帮助类 * @author luanhy * */public class EhCacheUserTokenHelper implements UserTokenOperHelper{/** * 对应ehcache.xml cache Name */private String userTokenCacheName ="userTokenCache";/** * ehcache缓存管理器 */private CacheManager cacheManager;public String getUserToken(String userCode){Cache cache = getUserTokenCache();if (cache == null) {return null;}else{Element element = cache.get(userCode);List keys = cache.getKeys();for (Object object : keys) {System.out.println(object);}if(element == null){return null;}else{Object objectValue = element.getObjectValue();if(objectValue == null){return null;}else{return (String)objectValue;}}}}public Cache getUserTokenCache(){Cache cache = cacheManager.getCache(userTokenCacheName);return cache;}public void updateUserToken(String userCode,String token,long seconds){Cache cache = getUserTokenCache();Element e = new Element(userCode, token);e.setTimeToLive(new Long(seconds).intValue());cache.put(e);List keys = cache.getKeys();for (Object object : keys) {System.out.println(object);}}public void deleteUserToken(String userCode){Cache cache = getUserTokenCache();cache.remove(userCode);}public String getUserTokenCacheName() {return userTokenCacheName;}public void setUserTokenCacheName(String userTokenCacheName) {this.userTokenCacheName = userTokenCacheName;}public CacheManager getCacheManager() {return cacheManager;}public void setCacheManager(CacheManager cacheManager) {this.cacheManager = cacheManager;}}
7.TokenManager 对token进行操作的接口
package com.lhy.common.shiro.token.manager;import com.lhy.common.shiro.token.StatelessToken;/** * 对token进行操作的接口 * @author luanhy */public interface TokenManager { /** * 创建一个token关联上指定用户 * @param userCode 指定用户的id * @return 生成的token */ public StatelessToken createToken(String userCode); /** * 检查token是否有效 * @param statelessToken * @return 是否有效 */ public boolean checkToken(StatelessToken statelessToken); /** * 检查身份是否有效 * @param model token * @return 是否有效 */ public boolean check(String authentication); /** * 从字符串中解析token * @param authentication 加密后的字符串 * @return */ public StatelessToken getToken(String authentication); /** * 清除token * @param userCode 登录用户的id */ public void deleteToken(String userCode);}
9.token管理抽象类
package com.lhy.common.shiro.token.manager;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.util.StringUtils;import com.lhy.common.shiro.token.StatelessToken;import com.lhy.common.shiro.token.helper.UserTokenOperHelper;public abstract class AbstractTokenManager implements TokenManager{/** * 失效时间 单位秒 */protected long expirateTime;protected final Logger logger = LoggerFactory.getLogger(AbstractTokenManager.class);protected String userTokenPrefix ="token_";protected UserTokenOperHelper userTokenOperHelper;//protected LoginFlagOperHelper loginFlagOperHelper;@Overridepublic StatelessToken createToken(String userCode) {StatelessToken tokenModel = null;String token = userTokenOperHelper.getUserToken(userTokenPrefix+userCode);if(StringUtils.isEmpty(token)){token = createStringToken(userCode);}userTokenOperHelper.updateUserToken(userTokenPrefix+userCode, token, expirateTime);tokenModel = new StatelessToken(userCode, token);return tokenModel;}public abstract String createStringToken(String userCode);protected boolean checkMemoryToken(StatelessToken model) {if(model == null){return false;}String userCode = (String)model.getPrincipal();String credentials = (String)model.getCredentials();String token = userTokenOperHelper.getUserToken(userTokenPrefix+userCode);if (token == null || !credentials.equals(token)) {return false;}return true;}@Overridepublic StatelessToken getToken(String authentication){if(StringUtils.isEmpty(authentication)){return null;}String[] au = authentication.split("_");if (au.length <=1) {return null;}String userCode = au[0];StringBuilder sb = new StringBuilder();for (int i = 1; i < au.length; i++) {sb.append(au[i]);if(i<au.length-1){sb.append("_");}}return new StatelessToken(userCode, sb.toString());}@Overridepublic boolean check(String authentication) {StatelessToken token = getToken(authentication);if(token == null){return false;}return checkMemoryToken(token);}@Overridepublic void deleteToken(String userCode) {userTokenOperHelper.deleteUserToken(userTokenPrefix+userCode);}public long getExpirateTime() {return expirateTime;}public void setExpirateTime(long expirateTime) {this.expirateTime = expirateTime;}public UserTokenOperHelper getUserTokenOperHelper() {return userTokenOperHelper;}public void setUserTokenOperHelper(UserTokenOperHelper userTokenOperHelper) {this.userTokenOperHelper = userTokenOperHelper;}//public LoginFlagOperHelper getLoginFlagOperHelper() {//return loginFlagOperHelper;//}//public void setLoginFlagOperHelper(LoginFlagOperHelper loginFlagOperHelper) {//this.loginFlagOperHelper = loginFlagOperHelper;//}}
10.token默认管理类
package com.lhy.common.shiro.token.manager.impl;import java.util.UUID;import com.lhy.common.shiro.token.StatelessToken;import com.lhy.common.shiro.token.manager.AbstractTokenManager;/** * 默认token管理实现类 * @author luanhy * */public class DefaultTokenManagerImpl extends AbstractTokenManager{@Overridepublic String createStringToken(String userCode) {//创建简易的32为uuidreturn UUID.randomUUID().toString().replace("-", "");}@Overridepublic boolean checkToken(StatelessToken model) {return super.checkMemoryToken(model);}}
package com.lhy.auth.service;import java.util.ArrayList;import java.util.List;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.annotation.CacheConfig;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;import com.lhy.common.mapper.WxUserMapper;import com.lhy.common.model.WxUser;import com.lhy.common.shiro.service.AuthorizationService;import com.lhy.common.shiro.service.PrincipalService;/** * Copyright: Copyright (c) 2017 wisedu * * @ClassName: MyAuthService.java * @Description: 具体权限服务 * * @version: v1.0.0 * @author: hyluan * @date: 2017年5月9日 下午4:12:16 * * Modification History: * Date Author Version Description *---------------------------------------------------------* * 2017年5月9日 hyluan v1.0.0 修改原因 */@Service@CacheConfig(cacheNames="role")public class MyAuthService implements AuthorizationService {private final Logger logger = LoggerFactory.getLogger(this.getClass());@Autowiredprivate WxUserMapper userMapper;@Override@Cacheablepublic List<String> selectRoles(String principal) {List<String> roles = new ArrayList<String>();//从数据库获取权限并设置logger.info("add roles");if("admin".equals(principal)){roles.add("admin");roles.add("vistor");}return roles;}}
12.具体用户服务
package com.lhy.api;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.annotation.CacheConfig;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;import com.lhy.common.mapper.WxUserMapper;import com.lhy.common.model.WxUser;import com.lhy.common.shiro.service.PrincipalService;@Service@CacheConfig(cacheNames="wxUser")public class UserService implements PrincipalService<WxUser>{@Autowiredprivate WxUserMapper userMapper;@Cacheablepublic WxUser getUserByUserCode(String userCode){WxUser user = new WxUser();user.setUserCode(userCode);return userMapper.selectOne(user);}@Overridepublic WxUser select(String principal) {return this.getUserByUserCode(principal);}}
13.StatelessToken令牌类
package com.lhy.common.shiro.token;import org.apache.shiro.authc.AuthenticationToken;public class StatelessToken implements AuthenticationToken {/** * */private static final long serialVersionUID = 1L;private String userCode;private String token;public StatelessToken(String userCode, String token){this.userCode = userCode;this.token = token;}@Overridepublic Object getPrincipal() {return userCode;}@Overridepublic Object getCredentials() {return token;}public String getUserCode() {return userCode;}public String getToken() {return token;}}
14.登陆Controller
package com.lhy.api;import javax.servlet.http.HttpServletRequest;import org.apache.shiro.SecurityUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import com.lhy.common.model.WxUser;import com.lhy.common.shiro.service.PrincipalService;import com.lhy.common.shiro.token.StatelessToken;import com.lhy.common.shiro.token.manager.TokenManager;import com.lhy.common.shiro.util.RequestUtil;@RestController@RequestMapping("/login")public class LoginController{@Autowiredprivate PrincipalService<WxUser> principalService;@Autowiredprivate TokenManager tokenManager;protected static final Logger logger = LoggerFactory.getLogger(LoginController.class);@RequestMapping(value = "",method = RequestMethod.POST)public StatelessToken login(String userCode, String password) {logger.info("userCode:"+userCode);WxUser usr = principalService.select(userCode);if (usr == null) {return new StatelessToken(userCode, "valid user");}if(!password.equals(usr.getPwd())){return new StatelessToken(userCode, "valid user password");}//成功穿件token返回给客户端保存StatelessToken createToken = tokenManager.createToken(userCode);return createToken;}@RequestMapping(value = "/logout",method = RequestMethod.GET)public String logout(HttpServletRequest request) {String authorization = RequestUtil.newInstance().getRequestHeader(request,"authorization");StatelessToken token = tokenManager.getToken(authorization);if(token!= null){tokenManager.deleteToken(token.getUserCode());}SecurityUtils.getSubject().logout();logger.info("用户登出");return "logout success";}}
function submit() {var userCode =$("#userCode").val();var password =$("#password").val();$("#ff").form("submit", {url : contextPath + "/login",onSubmit : function(param) {var ret = $(this).form('validate');if (ret) {$("#submit").linkbutton("disable");}return ret;},success : function(data) {$('#submit').linkbutton('enable');data = JSON.parse(data);if (data.token.indexOf("valid user") < 0) {//把用户编码和token保存在sessionStorage sessionStorage.setItem('userCode',data.userCode); sessionStorage.setItem('authorization',data.token); location.href=contextPath+"/html/user-notsafe.html";} else {$.messager.alert('提示', '账号或密码错误', 'info');}}});}
15,user-notesafe.html
api/users/admin3是一个简单的restful api接口获取用户
$(function() {$.ajax({url : contextPath+'/api/users/admin3',type : 'get',dataType : 'json',success : function(data) {alert(JSON.stringify(data));},error : function(e) {alert(e.responseText);}});$("#logout").click(function(){logOut();});//使用ajaxSetup 统一设置请求头$.ajaxSetup({cache: false, contentType:"application/x-www-form-urlencoded;charset=utf-8", beforeSend: function (xhr) { var authorization = localStorage.getItem('authorization'); var userCode =localStorage.getItem('userCode'); xhr.setRequestHeader("authorization", userCode+"_"+authorization); }, complete:function(XMLHttpRequest,textStatus){ } }); })function logOut(){$.ajax({url : contextPath+'/login/logout',type : 'get',success : function(data) {alert(JSON.stringify(data));这里不删除也没有关系,服务器端已经失效了该token//sessionStorage.removeItem('userCode');//sessionStorage.removeItem('authorization');location.href=contextPath+"/html/login-notsafe.html";},error : function(e) {alert(e.responseText);}});}
验证方式如下:
登录页面
完成后跳转到user-notsafe.html 正常情况下能获取到admin3用户信息。
登出后访问user-notsafe.html,已无法访问
即实现了springboot和无状态shiro的集成
后记
此种鉴权方式 token是从服务端返回给客户端,并且在客户端浏览器中保留,只要拦截到了该token,任意一台客户端端在请求中设置token,就可以正常的请求数据,这是非常不安全的,生产环境不建议这么使用。细心的朋友可能已经发现, shiroConfig tokenManager方法中引入了一段注释RaFilterJwtTokenManagerImpl 使用了国际规范的jwt token认证,通过token+共享密钥+黑名单方式控制鉴权,只要没有密钥,token还是很难破解的。下一篇博客讲讲解基于jwt的安全性的spring-boot shiro token鉴权。 1 0
- SpringBoot集成篇(一)无状态shiro
- springboot集成shiro(一)
- Shiro学习(20)无状态Web应用集成
- Shiro学习(20)无状态Web应用集成
- Spring Boot集成无状态Shiro
- SpringBoot集成shiro
- springboot集成shiro
- Spring Boot集成无状态Shiro--内容详细介绍
- shiro框架之无状态Web应用集成 二十
- SpringBoot集成shiro和ehcache
- Springboot 集成Shiro自定义Filter
- shiro无状态配置成功
- shiro无状态学习---(1)
- 第二十章 无状态Web应用集成——《跟我学Shiro》
- 第二十章 无状态Web应用集成——《跟我学Shiro》
- springboot集成shiro 实现权限控制
- springBoot集成shiro+redis遇到的坑
- CAS单点登录-客户端集成(shiro、springboot、jwt、pac4j)(十)
- dubbo提供方与消费方的三种连接方式
- A*
- oracle 数据库 50道经典例题
- s5pv210——LCD的原理和实战
- 大数相乘
- SpringBoot集成篇(一)无状态shiro
- mysql索引总结
- Android的异或加密解密算法解析
- 解析算术表达式
- C++PRIMER第一轮复习第九章 顺序容器
- git上传项目到github的常用命令
- 【java证书】JDK自带工具keytool生成ssl证书(第三步clinet有误,是client1)
- Glide
- JS对象和JSON