Shiro統合JWT無状態認証メカニズム(Token)
17427 ワード
まず大まかな手順を述べる JWTツールクラス、これはネットで探しています. はrealmをカスタマイズし、AuthorizingRealmを継承し、認証と認可の2つの方法 を書き換える.カスタムfilter、BasichttpAuthenticationFilter を継承 JWTを使用しているので、セッションを直接無効にします. ポイント、RedisにはJWTToken情報(JWTの制御可能性) が保存されている
ユーザーがログインした後、jwtToken(tokenに5分などの期限切れの時間が保存されている)に戻り、同時にtokenをredisに保存し(30分などの自動削除時間を設定)、その後、ユーザーはこのTokenを持って他のインタフェースにアクセスし、redisにこのtokenがない場合、tokenはすでに失効し、tokenが5分を超え(期限切れ)てredis内のこのtokenがまだ存在する場合、tokenを再生成してユーザーに返し、redis内のtokenを更新すると、tokenの時間が5分延長されます.Redisにもtokenが保存されているのでTユーザー、統計オンラインユーザーなどの機能を実現できます
まずサードパーティのjarパッケージを追加します(githubにコードが付いているので貼らない)
https://github.com/gemingyi/shiro_demo
一、JWTツールクラス(ネットで探した)はrealmをカスタマイズし、AuthorizingRealmを継承し、認証と認可の2つの方法 を書き換える.セッションを無効にする shiro構成クラス
コントロール層コード
ユーザーがログインした後、jwtToken(tokenに5分などの期限切れの時間が保存されている)に戻り、同時にtokenをredisに保存し(30分などの自動削除時間を設定)、その後、ユーザーはこのTokenを持って他のインタフェースにアクセスし、redisにこのtokenがない場合、tokenはすでに失効し、tokenが5分を超え(期限切れ)てredis内のこのtokenがまだ存在する場合、tokenを再生成してユーザーに返し、redis内のtokenを更新すると、tokenの時間が5分延長されます.Redisにもtokenが保存されているのでTユーザー、統計オンラインユーザーなどの機能を実現できます
まずサードパーティのjarパッケージを追加します(githubにコードが付いているので貼らない)
https://github.com/gemingyi/shiro_demo
一、JWTツールクラス(ネットで探した)
@Component
public class JWTUtil {
//token
private static String tokenExpireTime ;
@Value("${jwt.tokenExpireTime}")
public void setTokenExpireTime(String tokenExpireTime) {
JWTUtil.tokenExpireTime = tokenExpireTime;
}
/**
* token
*
* @param token
* @param secret
* @return
*/
public static Map verify(String token, String username, String secret) {
Map result = new HashMap(2);
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("userName", username)
.build();
DecodedJWT jwt = verifier.verify(token);
result.put("isSuccess", true);
result.put("ex", null);
// return true;
} catch (Exception exception) {
result.put("isSuccess", false);
result.put("exception", exception);
// return false;
}
return result;
}
/**
* token secret
*
* @return token
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("userName").asString();
} catch (JWTDecodeException e) {
e.printStackTrace();
return null;
}
}
/**
* ,30min
*
* @param username
* @param secret
* @return token
*/
public static String sign(String username, String secret) {
try {
//token
Date date = new Date(System.currentTimeMillis() + (Long.parseLong(tokenExpireTime) * 60 * 1000));
// MD5
// Object md5Password = new SimpleHash("MD5", secret, username, 2);
// Algorithm algorithm = Algorithm.HMAC256(String.valueOf(md5Password));
Algorithm algorithm = Algorithm.HMAC256(secret);
// username
return JWT.create()
.withClaim("userName", username)
.withExpiresAt(date)
.sign(algorithm);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public class MyRealm extends AuthorizingRealm {
@Autowired
private IUserService userService;
@Autowired
RedisTemplate redisTemplate;
/**
* !, , Shiro
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JWTToken;
}
/**
*
*
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName = JWTUtil.getUsername(principals.toString());
SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
Map map = null;
try {
map = this.userService.getRolesAndPermissionsByUserName(userName);
auth.setRoles((Set) map.get("allRoles"));
auth.setStringPermissions((Set) map.get("allPermissions"));
} catch (Exception e) {
e.printStackTrace();
}
return auth;
}
/**
*
*
* @param auth
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
String token = (String) auth.getCredentials();
// userName,
String userName = JWTUtil.getUsername(token);
User vo = this.userService.getUserByUserName(userName);
String redisUserInfo = (String) redisTemplate.opsForValue().get("token_jwt_" + userName);
Map result = JWTUtil.verify(token, userName, vo.getPassword());
Exception exception = (Exception) result.get("exception");
if (vo == null) {
throw new UnknownAccountException(" !");
} else if (vo.getLock() == null || vo.getLock().equals(1)) {
throw new UnknownAccountException(" !");
} else if (exception != null && exception instanceof SignatureVerificationException) {
throw new AuthenticationException("Token (Token incorrect.)!");
} else if (exception != null && exception instanceof TokenExpiredException) {
throw new AuthenticationException("Token (Token expired.)!");
// T
} else if(StringUtils.isEmpty(redisUserInfo)){
throw new AuthenticationException("Token (Token invalid.)!");
}else {
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(token, token, vo.getUserName());
return authcInfo;
}
}
}
、 filter, BasicHttpAuthenticationFilter
@Component
public class JWTFilter extends BasicHttpAuthenticationFilter {
@Value("${jwt.anonymous.urls}")
private String anonymousStr;
@Autowired
RedisTemplate redisTemplate;
@Autowired
private IUserService userService;
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
String contextPath = WebUtils.getPathWithinApplication(WebUtils.toHttp(servletRequest));
if (!StringUtils.isEmpty(anonymousStr)) {
String[] anonUrls = anonymousStr.split(",");
// url
for (int i = 0; i < anonUrls.length; i++) {
if (contextPath.contains(anonUrls[i])) {
return true;
}
}
}
// token
AuthenticationToken token = this.createToken(servletRequest, servletResponse);
if (token.getPrincipal() == null) {
handler401(servletResponse, CodeAndMsgEnum.UNAUTHENTIC.getcode(), CodeAndMsgEnum.UNAUTHENTIC.getMsg());
return false;
} else {
try {
this.getSubject(servletRequest, servletResponse).login(token);
return true;
} catch (Exception e) {
String msg = e.getMessage();
//token
if (msg.contains("incorrect")) {
handler401(servletResponse, CodeAndMsgEnum.UNAUTHENTIC.getcode(), msg);
return false;
//token
} else if (msg.contains("expired")) {
// token
if (this.refreshToken(servletRequest, servletResponse)) {
return true;
} else {
handler401(servletResponse, CodeAndMsgEnum.UNAUTHENTIC.getcode(), "token , ");
return false;
}
}
handler401(servletResponse, CodeAndMsgEnum.UNAUTHENTIC.getcode(), msg);
return false;
}
}
}
/**
* AccessToken , RefreshToken , AccessToken
*
* @param servletRequest
* @param servletResponse
* @return
*/
private boolean refreshToken(ServletRequest servletRequest, ServletResponse servletResponse) {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// header,tokenStr
String oldToken = request.getHeader("Authorization");
String userName = JWTUtil.getUsername(oldToken);
String key = CommonConstant.JWT_TOKEN + userName;
// redis tokenStr
String redisUserInfo = (String) redisTemplate.opsForValue().get(key);
if (redisUserInfo != null) {
if (oldToken.equals(redisUserInfo)) {
User vo = this.userService.getUserByUserName(userName);
// token( )
String newTokenStr = JWTUtil.sign(vo.getUserName(), vo.getPassword());
JWTToken jwtToken = new JWTToken(newTokenStr);
userService.addTokenToRedis(userName, newTokenStr);
SecurityUtils.getSubject().login(jwtToken);
response.setHeader("Authorization", newTokenStr);
return true;
}
}
return false;
}
/**
* token
*
* @param response
* @param code
* @param msg
*/
private void handler401(ServletResponse response, int code, String msg) {
try {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=utf-8");
httpResponse.getWriter().write("{\"code\":" + code + ", \"msg\":\"" + msg + "\"}");
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
/**
*
*
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", "*");
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", "Authorization,Origin,X-Requested-With,Content-Type,Accept");
// option , option
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(httpServletRequest, httpServletResponse);
}
@Override
protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String token = request.getHeader("Authorization");
return new JWTToken(token);
}
}
public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {
@Override
public Subject createSubject(SubjectContext context) {
// session
context.setSessionCreationEnabled(false);
return super.createSubject(context);
}
}
@Configuration
public class ShiroConfiguration {
@Bean(name = "myRealm")
public MyRealm myRealm() {
MyRealm myShiroRealm = new MyRealm();
// myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
myShiroRealm.setCachingEnabled(true);
// myShiroRealm.setCacheManager(redisCacheManager());
return myShiroRealm;
}
@Bean(name = "subjectFactory")
public StatelessDefaultSubjectFactory subjectFactory() {
StatelessDefaultSubjectFactory statelessDefaultSubjectFactory = new StatelessDefaultSubjectFactory();
return statelessDefaultSubjectFactory;
}
@Bean(name = "sessionManager")
public DefaultSessionManager sessionManager() {
DefaultSessionManager sessionManager = new DefaultSessionManager();
sessionManager.setSessionValidationSchedulerEnabled(false);
return sessionManager;
}
@Bean(name = "defaultSessionStorageEvaluator")
public DefaultSessionStorageEvaluator defaultSessionStorageEvaluator () {
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
return defaultSessionStorageEvaluator;
}
@Bean(name = "subjectDAO")
public DefaultSubjectDAO subjectDAO(@Qualifier("defaultSessionStorageEvaluator")DefaultSessionStorageEvaluator defaultSessionStorageEvaluator) {
DefaultSubjectDAO defaultSubjectDAO = new DefaultSubjectDAO();
defaultSubjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
return defaultSubjectDAO;
}
@Bean(name = "securityManager")
public SecurityManager securityManager(@Qualifier("myRealm")MyRealm myRealm, @Qualifier("subjectDAO")DefaultSubjectDAO
subjectDAO, @Qualifier("sessionManager")DefaultSessionManager sessionManager, @Qualifier("subjectFactory")StatelessDefaultSubjectFactory subjectFactory) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
securityManager.setSubjectDAO(subjectDAO);
securityManager.setSubjectFactory(subjectFactory);
securityManager.setSessionManager(sessionManager);
return securityManager;
}
@Bean(name = "jwtFilter")
public JWTFilter jwtFilter() {
return new JWTFilter();
}
@Bean
public FilterRegistrationBean delegatingFilterProxy(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
DelegatingFilterProxy proxy = new DelegatingFilterProxy();
proxy.setTargetFilterLifecycle(true);
proxy.setTargetBeanName("shiroFilter");
filterRegistrationBean.setFilter(proxy);
return filterRegistrationBean;
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager")SecurityManager securityManager, @Qualifier("jwtFilter")JWTFilter jwtFilter) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//
Map filters = new HashedMap(2);
filters.put("jwtFilter", jwtFilter);
shiroFilterFactoryBean.setFilters(filters);
//
Map filterChainDefinitionMap = new LinkedHashMap();
filterChainDefinitionMap.put("/**", "jwtFilter");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean(name = "advisorAutoProxyCreator")
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean(name = "authorizationAttributeSourceAdvisor")
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
コントロール層コード
@RequestMapping(value = "/userLogin", method = RequestMethod.POST)
public Map ajaxLogin(User userInfo, HttpServletResponse response) {
Map result = new HashMap<>(4);
User vo = this.userService.getUserByUserName(userInfo.getUserName());
if (null != vo && vo.getPassword().equals(userInfo.getPassword())) {
String tokenStr = JWTUtil.sign(userInfo.getUserName(), userInfo.getPassword());
userService.addTokenToRedis(userInfo.getUserName(), tokenStr);
result.put("code", CodeAndMsgEnum.SUCCESS.getcode());
result.put("msg", " !");
response.setHeader("Authorization", tokenStr);
} else {
result.put("code", CodeAndMsgEnum.ERROR.getcode());
result.put("msg", " !");
}
return result;
}