提示:本配置的预期读者是熟悉shiro的session配置的开发者.如果不熟悉我有其他博客关于session式,可以查看
shiro默认是以session来确权的
现在以jwt的方式使用,那么登录方法就不再subject.login,而是返回jwt字符串,我们将userid放进去.
@GetMapping("login")
public String login(String username,String password) throws IllegalArgumentException, JWTCreationException, UnsupportedEncodingException{
//数据库查询
User user = login(username,password)....
//把userid附上,生成jwt返回给前台
String sign = JWT.create().withClaim("userid", user.getId())
.withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*24*3))
.sign(Algorithm.HMAC256("123456"));
return sign;
}
jwt的话需要做一个jwtfilter,进行解析jwt字串.然后登录
这里继承自AccessControlFilter, 核心方法在onAccessDenied里面.
@Slf4j
public class JwtFilter extends AccessControlFilter {
/*
* 1. 返回true,shiro就直接允许访问url
* 2. 返回false,shiro才会根据onAccessDenied的方法的返回值决定是否允许访问url
* */
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return false;
}
/**
* 返回结果为true表明登录通过
*/
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
log.warn("onAccessDenied 方法被调用");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String token = request.getHeader("token");
if (StringUtils.isBlank(token)){
request.getRequestDispatcher("/error").forward(request, response);//如果没有token跳转到错误提示
return false;
}
try {
JWT.require(Algorithm.HMAC256("123456")).build().verify(token);
} catch (Exception e) {
request.getRequestDispatcher("/error").forward(request, response);//如果token校验错误跳转到错误提示
return false;
}
DecodedJWT decode = JWT.decode(token);
Integer userid = decode.getClaim("userid").asInt();//获取userid
//构造出一个jwtToken
JwtToken jwtToken = new JwtToken(userid, token);
getSubject(servletRequest, servletResponse).login(jwtToken);//登录
return true;
}
}
不再使用usernamepasswordtoken,而是新建了一个jwttoken
public class JwtToken implements AuthenticationToken{
private Integer id;
private String token;
public JwtToken() {
super();
}
public JwtToken(Integer id, String token) {
super();
this.id = id;
this.token = token;
}
@Override
public Object getPrincipal() {
return id;
}
@Override
public Object getCredentials() {
return token;
}
...getter/setter
}
了解shiro的都知道核心realm.其实realm都差不多
@Slf4j
public class CustomRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.....set一堆权限
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户信息
Integer userid = (Integer)authenticationToken.getPrincipal();
log.info("认证.....{}",userid);
User user = getById(userid)....
if (user == null){
return null;
}
return new SimpleAuthenticationInfo(userid,authenticationToken.getCredentials(),getName());
}
//如果是自定义token,则需要重写支持方法
@Override
public boolean supports(AuthenticationToken token) {
if (token instanceof JwtToken)
return true;
return false;
}
}
注意一些配置,这里禁用了session
@Component
public class JwtDefaultSubjectFactory extends DefaultWebSubjectFactory {
@Override
public Subject createSubject(SubjectContext context) {
// 不创建 session
context.setSessionCreationEnabled(false);
return super.createSubject(context);
}
}
虽然有禁用session,但是如果不配置下面的,依然会检查session,但是由于禁用了session,所以会报错.
@Component
public class NoSessionSubjectDAO extends DefaultSubjectDAO{
@Override
protected boolean isSessionStorageEnabled(Subject subject) {
// TODO Auto-generated method stub
return false;
}
}
shiro的核心配置
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Shiro的核心配置类,用来整合shiro框架
*/
@Configuration
public class ShiroConfiguration {
//1.创建shiroFilter //负责拦截所有请求
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//给filter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
filters.put("jwt", new JwtFilter());//spring控制
shiroFilterFactoryBean.setFilters(filters );
//配置资源
Map<String,String> map = new LinkedHashMap<String,String>();//为什么是LinkedHashMap,因为有序sort
map.put("/login","anon");
map.put("/**", "jwt");
//默认认证界面路径
shiroFilterFactoryBean.setLoginUrl("/error-login");
shiroFilterFactoryBean.setUnauthorizedUrl("/error-author");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//2.创建安全管理器
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm,JwtDefaultSubjectFactory jwtDefaultSubjectFactory,NoSessionSubjectDAO noSessionSubjectDAO){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//给安全管理器设置
defaultWebSecurityManager.setRealm(realm);
defaultWebSecurityManager.setSubjectFactory(jwtDefaultSubjectFactory);//禁用session
defaultWebSecurityManager.setSubjectDAO(noSessionSubjectDAO );//不再保存session
return defaultWebSecurityManager;
}
//3.创建自定义realm
@Bean
public Realm getRealm(){
CustomRealm customerRealm = new CustomRealm();
return customerRealm;
}
/**
* 配置注解方式权限
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
= new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 配置注解方式权限
* @return
*/
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
}
结语:
能完美使用token