1 github:源码地址 2 shiro09 子工程登录成功时返回token,之后的请求携带token进行认证授权;
之前是认证一次,由session存储用户信息,
现在是使用jwt(无状态)每次请求都需要重新认证,所以禁用session
4.0.0 com.yzm shiro 0.0.1-SNAPSHOT ../pom.xml shiro09 0.0.1-SNAPSHOT jar shiro09 Demo project for Spring Boot com.yzm common 0.0.1-SNAPSHOT io.jsonwebtoken jjwt 0.9.1 org.crazycake shiro-redis 3.2.3 org.springframework.boot spring-boot-maven-plugin
项目结构
application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test3?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
username: root
password: root
mybatis-plus:
mapper-locations: classpath:/mapper
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
4 自定义拦截器JwtFilter
指定哪些url需要携带token并生成JwtToken
isAccessAllowed() 该拦截器的入口
createToken() 获取请求头的token并判断是否过期,未过期生成JwtToken
package com.yzm.shiro09.config;
import com.alibaba.fastjson.JSONObject;
import com.yzm.common.entity.HttpResult;
import com.yzm.shiro09.entity.JwtToken;
import com.yzm.shiro09.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class JwtFilter extends AuthenticatingFilter {
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
@Override
protected void postHandle(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,HEAD");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
boolean allowed = false;
try {
allowed = executeLogin(request, response);
} catch (IllegalStateException e) {
log.error("Not found any token");
} catch (Exception e) {
log.error("Error occurs when login", e);
}
return allowed || super.isPermissive(mappedValue);
}
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
AuthenticationToken token = this.createToken(request, response);
if (token == null) {
String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken must be created in order to execute a login attempt.";
throw new IllegalStateException(msg);
} else {
try {
Subject subject = this.getSubject(request, response);
subject.login(token);
return this.onLoginSuccess(token, subject, request, response);
} catch (AuthenticationException var5) {
return this.onLoginFailure(token, var5, request, response);
}
}
}
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
String jwtToken = JwtUtils.getTokenFromRequest(WebUtils.toHttp(request));
if (StringUtils.isNotBlank(jwtToken)) return new JwtToken(jwtToken);
return null;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
HttpResult result = new HttpResult(500, "请求失败", null);
response.getWriter().print(JSONObject.toJSONString(result, true));
response.getWriter().flush();
response.getWriter().close();
return false;
}
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
log.info("token认证成功");
if (token instanceof JwtToken) {
JwtToken jwtToken = (JwtToken) token;
String tokenStr = jwtToken.getToken();
if (JwtUtils.isExpired(tokenStr)) {
log.info("刷新token");
String username = (String) subject.getPrincipal();
Map map = new HashMap<>();
map.put(JwtUtils.USERNAME, username);
tokenStr = JwtUtils.generateToken(map);
}
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.setHeader("token", tokenStr);
}
return true;
}
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
log.info("token认证失败");
if (e instanceof IncorrectCredentialsException) {
log.error("刷新token");
JwtToken jwtToken = (JwtToken) token;
String tokenStr = jwtToken.getToken();
String username = JwtUtils.getUsernameFromToken(tokenStr);
Map map = new HashMap<>();
map.put(JwtUtils.USERNAME, username);
String newToken = JwtUtils.generateToken(map);
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.setHeader("token", newToken);
}
if (e instanceof UnknownAccountException) {
log.error("账号异常");
}
return false;
}
}
JwtFilter拦截器的处理逻辑跟登录接口是差不多的,生成对应的token并调用subject.login(token)
@PostMapping("doLogin")
@ResponseBody
public Object doLogin(@RequestParam String username, @RequestParam String password, boolean rememberMe) {
String token;
try {
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
if (rememberMe) usernamePasswordToken.setRememberMe(true);
Subject subject = SecurityUtils.getSubject();
subject.login(usernamePasswordToken);
// 生成token
Map map = new HashMap<>();
map.put(JwtUtils.USERNAME, username);
token = JwtUtils.generateToken(map);
} catch (IncorrectCredentialsException ice) {
return HttpResult.error("password error!");
} catch (UnknownAccountException uae) {
return HttpResult.error("username error!");
}
return HttpResult.ok(token);
}
在ShrioConfig#shiroFilter,注入到Shiro并使用
@Bean
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
shiroFilterFactoryBean.setLoginUrl("/login");
// 设置无权限时跳转的 url
shiroFilterFactoryBean.setUnauthorizedUrl("/401");
// 注入自定义拦截器
Map filters = new linkedHashMap<>();
filters.put("autht", new JwtFilter());
shiroFilterFactoryBean.setFilters(filters);
// url拦截规则
Map definitionMap = new linkedHashMap<>();
definitionMap.put("/home", "anon");
definitionMap.put("/register", "anon");
definitionMap.put("/login", "anon");
definitionMap.put("/doLogin", "anon");
definitionMap.put("/401", "anon");
definitionMap.put("/logout", "anon");
// 禁用session创建,还需要noSessionCreation
definitionMap.put("
public class JwtUtils implements Serializable {
private static final long serialVersionUID = 8527289053988618229L;
public static final String TOKEN_HEADER = "Authorization";
public static final String TOKEN_PREFIX = "Basic ";
public static final String USERNAME = Claims.SUBJECT;
public static final String PASSWORD = "password";
public static final String AUTHORITIES = "authorities";
private static final String SECRET = "abcdefg";
private static final String JWT_SECRET = "7786df7fc3a34e26a61c034d5ec8245d";
public static final long TOKEN_EXPIRED_TIME = 5 * 60 * 1000L;
public static final long TOKEN_REFRESH_TIME = 2 * 60 * 1000L;
public static String generateToken(Map claims) {
return generateToken(claims, 0L);
}
public static String generateToken(Map claims, long expireTime) {
if (expireTime <= 0L) expireTime = TOKEN_EXPIRED_TIME;
Map headMap = new HashMap<>();
headMap.put("typ", "JWT");
headMap.put("alg", "HS256");
return Jwts.builder()
.setHeader(headMap)
//JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击
.setId(UUID.randomUUID().toString())
//.setIssuer("该JWT的签发者,是否使用是可选的")
//.setSubject("该JWT所面向的用户,是否使用是可选的")
//.setAudience("接收该JWT的一方,是否使用是可选的")
//如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setClaims(claims)
//签发时间(token生成时间)
.setIssuedAt(new Date())
//生效时间(在指定时间之前令牌是无效的)
.setNotBefore(new Date())
//过期时间(在指定时间之后令牌是无效的)
.setExpiration(new Date(System.currentTimeMillis() + expireTime))
//设置签名使用的签名算法和签名使用的秘钥
// .signWith(SignatureAlgorithm.HS256, JWT_SECRET)
.signWith(SignatureAlgorithm.HS256, generalKey())
.compact();
}
public static Claims verifyToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
//签名秘钥
// .setSigningKey(JWT_SECRET)
.setSigningKey(generalKey())
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
// token过期是直接抛出异常的,但仍然可以获取到claims对象
claims = e.getClaims();
}
return claims;
}
public static SecretKey generalKey() {
byte[] encodedKey = base64.decodebase64(SECRET);
// return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return new SecretKeySpec(encodedKey, SignatureAlgorithm.HS256.getJcaName());
}
public static boolean isExpired(String token) {
Claims claims = verifyToken(token);
//和当前时间进行对比来判断是否过期
return claims.getExpiration().before(new Date());
}
public static String getUsernameFromToken(String token) {
Claims claims = verifyToken(token);
return claims.getSubject();
}
public static String getTokenFromRequest(HttpServletRequest request) {
String token = request.getHeader(TOKEN_HEADER);
if (token == null) token = request.getHeader("token");
if (StringUtils.isBlank(token)) return null;
if (token.startsWith(TOKEN_PREFIX)) {
token = token.substring(TOKEN_PREFIX.length());
}
return token;
}
}
6 认证和授权
package com.yzm.shiro09.config;
import com.yzm.shiro09.entity.Permissions;
import com.yzm.shiro09.entity.Role;
import com.yzm.shiro09.entity.User;
import com.yzm.shiro09.service.PermissionsService;
import com.yzm.shiro09.service.RoleService;
import com.yzm.shiro09.service.UserService;
import org.apache.shiro.authc.*;
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 java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class MyShiroRealm extends AuthorizingRealm {
private final UserService userService;
private final RoleService roleService;
private final PermissionsService permissionsService;
public MyShiroRealm(UserService userService, RoleService roleService, PermissionsService permissionsService) {
this.userService = userService;
this.roleService = roleService;
this.permissionsService = permissionsService;
}
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) principalCollection.getPrimaryPrincipal();
// 查询用户,获取角色ids
User user = userService.lambdaQuery().eq(User::getUsername, username).one();
List roleIds = Arrays.stream(user.getRIds().split(","))
.map(Integer::parseInt)
.collect(Collectors.toList());
// 查询角色,获取角色名、权限ids
List roles = roleService.listByIds(roleIds);
Set roleNames = new HashSet<>(roles.size());
Set permIds = new HashSet<>();
roles.forEach(role -> {
roleNames.add(role.getRName());
Set collect = Arrays.stream(
role.getPIds().split(",")).map(Integer::parseInt).collect(Collectors.toSet());
permIds.addAll(collect);
});
// 获取权限名称
List permissions = permissionsService.listByIds(permIds);
List permNames = permissions.stream().map(Permissions::getPName).collect(Collectors.toList());
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRoles(roleNames);
authorizationInfo.addStringPermissions(permNames);
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取用户名跟密码
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String username = usernamePasswordToken.getUsername();
// 也可以这样获取
//String username = (String) authenticationToken.getPrincipal();
//String password = new String((char[]) authenticationToken.getCredentials());
// 查询用户是否存在
User user = userService.lambdaQuery().eq(User::getUsername, username).one();
if (user == null) {
throw new UnknownAccountException();
}
return new SimpleAuthenticationInfo(
user.getUsername(),
user.getPassword(),
// 用户名 + 盐
new MySimpleByteSource(user.getUsername() + user.getSalt()),
getName()
);
}
}
JwtRealm.java
package com.yzm.shiro09.config;
import com.yzm.shiro09.entity.JwtToken;
import com.yzm.shiro09.entity.User;
import com.yzm.shiro09.service.UserService;
import com.yzm.shiro09.utils.JwtUtils;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class JwtRealm extends AuthorizingRealm {
private final UserService userService;
public JwtRealm(UserService userService) {
this.userService = userService;
}
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
JwtToken jwtToken = (JwtToken) authToken;
String token = jwtToken.getToken();
String username = JwtUtils.getUsernameFromToken(token);
if (username == null) {
throw new AuthenticationException("token过期,请重新登录");
}
User user = userService.lambdaQuery().eq(User::getUsername, username).one();
if (user == null) {
throw new AuthenticationException("账号异常");
}
return new SimpleAuthenticationInfo(
username,
token,
getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return new SimpleAuthorizationInfo();
}
}
JwtRealm的凭证信息是token,所以我们需要自定义凭证匹配器JwtCredentialsMatcher,主要就是校验下token是否过期
package com.yzm.shiro09.config;
import com.yzm.shiro09.entity.JwtToken;
import com.yzm.shiro09.utils.JwtUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
public class JwtCredentialsMatcher implements CredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
if (authenticationToken instanceof JwtToken) {
String token = (String) authenticationInfo.getCredentials();
return !JwtUtils.isExpired(token);
}
return false;
}
}
7 ShiroConfig 配置类
package com.yzm.shiro09.config;
import com.yzm.shiro09.service.PermissionsService;
import com.yzm.shiro09.service.RoleService;
import com.yzm.shiro09.service.UserService;
import com.yzm.shiro09.utils.EncryptUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.DefaultSessionStorageevaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionStorageevaluator;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.cookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.cookie;
import org.apache.shiro.web.servlet.Simplecookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import javax.servlet.Filter;
import java.util.Arrays;
import java.util.linkedHashMap;
import java.util.Map;
import java.util.Properties;
@Configuration
public class ShiroConfig {
private final UserService userService;
private final RoleService roleService;
private final PermissionsService permissionsService;
public ShiroConfig(UserService userService, RoleService roleService, PermissionsService permissionsService) {
this.userService = userService;
this.roleService = roleService;
this.permissionsService = permissionsService;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName(EncryptUtils.ALGORITHM_NAME);
hashedCredentialsMatcher.setHashIterations(EncryptUtils.HASH_ITERATIONS);
//hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return hashedCredentialsMatcher;
}
@Bean
public MyShiroRealm shiroRealm() {
MyShiroRealm shiroRealm = new MyShiroRealm(userService, roleService, permissionsService);
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
// 开启缓存
shiroRealm.setCachingEnabled(true);
//启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
shiroRealm.setAuthenticationCachingEnabled(true);
//缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
shiroRealm.setAuthenticationCacheName("authenticationCache");
//启用授权缓存,即缓存AuthorizationInfo信息,默认false
shiroRealm.setAuthorizationCachingEnabled(true);
//缓存AuthorizationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
shiroRealm.setAuthorizationCacheName("authorizationCache");
return shiroRealm;
}
@Bean
public JwtRealm jwtRealm() {
JwtRealm jwtRealm = new JwtRealm(userService);
jwtRealm.setCredentialsMatcher(new JwtCredentialsMatcher());
return jwtRealm;
}
@Bean
public cookie simplecookie() {
Simplecookie cookie = new Simplecookie("rememberMe");
//设为true后,只能通过http访问,javascript无法访问
//防止xss读取cookie
cookie.setHttpOnly(true);
cookie.setPath("/");
//存活时间,单位秒;-1表示关闭浏览器该cookie失效
cookie.setMaxAge(-1);
return cookie;
}
@Bean
public cookieRememberMeManager rememberMeManager() {
cookieRememberMeManager rememberMeManager = new cookieRememberMeManager();
rememberMeManager.setcookie(simplecookie());
//cookie加密的密钥
//rememberMeManager.setCipherKey(base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return rememberMeManager;
}
@Bean
protected SessionStorageevaluator sessionStorageevaluator() {
DefaultSessionStorageevaluator sessionStorageevaluator = new DefaultSessionStorageevaluator();
sessionStorageevaluator.setSessionStorageEnabled(false);
return sessionStorageevaluator;
}
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost("127.0.0.1:6379");
redisManager.setPassword("1234");
redisManager.setDatabase(0);
return redisManager;
}
@Bean
public RedisCacheManager redisCacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
// redis中针对不同用户缓存
redisCacheManager.setPrincipalIdFieldName("username");
// 用户权限信息缓存时间
redisCacheManager.setExpire(300);
return redisCacheManager;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 配置单个realm
//securityManager.setRealm(shiroRealm());
// 验证器
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
securityManager.setAuthenticator(authenticator);
// 自定义realm 一定要放在securityManager.authorizer赋值之后(因为调用setRealms会将realms设置给authorizer
securityManager.setRealms(Arrays.asList(shiroRealm(), jwtRealm()));
// 记住我
securityManager.setRememberMeManager(rememberMeManager());
// 缓存
securityManager.setCacheManager(redisCacheManager());
// 关闭shiro自带的session
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
subjectDAO.setSessionStorageevaluator(sessionStorageevaluator());
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
@Bean
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
shiroFilterFactoryBean.setLoginUrl("/login");
// 设置无权限时跳转的 url
shiroFilterFactoryBean.setUnauthorizedUrl("/401");
// 注入自定义拦截器
Map filters = new linkedHashMap<>();
filters.put("autht", new JwtFilter());
shiroFilterFactoryBean.setFilters(filters);
// url拦截规则
Map definitionMap = new linkedHashMap<>();
definitionMap.put("/home", "anon");
definitionMap.put("/register", "anon");
definitionMap.put("/login", "anon");
definitionMap.put("/doLogin", "anon");
definitionMap.put("/401", "anon");
definitionMap.put("/logout", "anon");
// 禁用session创建,还需要noSessionCreation
definitionMap.put("
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
// 登录后没有权限跳转到/401
properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/401");
// 未登录访问接口跳转到/login
properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "/login");
simpleMappingExceptionResolver.setExceptionMappings(properties);
return simpleMappingExceptionResolver;
}
}
测试
登录yzm
访问/user/select
在这里我们设置了不创建Session
@Bean
public ShiroFilterFactoryBean shiroFilter() {
...
// 注入自定义拦截器
Map filters = new linkedHashMap<>();
filters.put("autht", new JwtFilter());
shiroFilterFactoryBean.setFilters(filters);
// url拦截规则
...
// 禁用session创建,还需要noSessionCreation
definitionMap.put("
@Slf4j
public class MultiRealmAuthenticator extends ModularRealmAuthenticator {
@Override
protected AuthenticationInfo doMultiRealmAuthentication(Collection realms, AuthenticationToken token)
throws AuthenticationException {
AuthenticationStrategy strategy = getAuthenticationStrategy();
AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);
if (log.isTraceEnabled()) {
log.trace("Iterating through {} realms for PAM authentication", realms.size());
}
AuthenticationException authenticationException = null;
for (Realm realm : realms) {
aggregate = strategy.beforeAttempt(realm, token, aggregate);
if (realm.supports(token)) {
log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);
AuthenticationInfo info = null;
try {
info = realm.getAuthenticationInfo(token);
} catch (AuthenticationException e) {
authenticationException = e;
if (log.isDebugEnabled()) {
String msg = "Realm [" + realm
+ "] threw an exception during a multi-realm authentication attempt:";
log.debug(msg, e);
}
}
aggregate = strategy.afterAttempt(realm, token, info, aggregate, authenticationException);
} else {
log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token);
}
}
if (authenticationException != null) {
throw authenticationException;
}
aggregate = strategy.afterAllAttempts(token, aggregate);
return aggregate;
}
}
在ShiroConfig#securityManager
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 配置单个realm
//securityManager.setRealm(shiroRealm());
// 自定义验证器
ModularRealmAuthenticator authenticator = new MultiRealmAuthenticator();
authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
securityManager.setAuthenticator(authenticator);
...
return securityManager;
}



