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版本
org.apache.shiro
shiro-core
1.3.2
org.apache.shiro
shiro-web
1.3.2
org.apache.shiro
shiro-spring
1.3.2
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
*
*/
@Configuration
public 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 filters = new HashMap();
//无需增加 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 filterChainDefinitionMap = new LinkedHashMap();
//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生命周期,
* 该过滤器会执行其他过滤器拦截过的路径
* @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;
}
}
shiro.token.key=helloworld
shiro.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);
@Autowired
private TokenManager tokenManager;
public TokenManager getTokenManager() {
return tokenManager;
}
public void setTokenManager(TokenManager tokenManager) {
this.tokenManager = tokenManager;
}
@Override
protected 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;
}
// 获取无状态Token
StatelessToken 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;
}
@Override
protected 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;
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof StatelessToken;
}
private AuthorizationService authorizationService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//根据用户名查找角色,请根据需求实现
String userCode = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
List selectRoles = authorizationService.selectRoles(userCode);
authorizationInfo.addRoles(selectRoles);
return authorizationInfo;
}
@Override
protected 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
*/
public interface PrincipalService {
/**
* 根据用户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 {
@Override
public 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);
}
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;
}
}
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;
@Override
public 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;
}
@Override
public 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
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{
@Override
public String createStringToken(String userCode) {
//创建简易的32为uuid
return UUID.randomUUID().toString().replace("-", "");
}
@Override
public 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());
@Autowired
private WxUserMapper userMapper;
@Override
@Cacheable
public List selectRoles(String principal) {
List roles = new ArrayList();
//从数据库获取权限并设置
logger.info("add roles");
if("admin".equals(principal)){
roles.add("admin");
roles.add("vistor");
}
return roles;
}
}
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{
@Autowired
private WxUserMapper userMapper;
@Cacheable
public WxUser getUserByUserCode(String userCode){
WxUser user = new WxUser();
user.setUserCode(userCode);
return userMapper.selectOne(user);
}
@Override
public 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;
}
@Override
public Object getPrincipal() {
return userCode;
}
@Override
public Object getCredentials() {
return token;
}
public String getUserCode() {
return userCode;
}
public String getToken() {
return token;
}
}
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{
@Autowired
private PrincipalService principalService;
@Autowired
private 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);
}
});
}