概述
采用json web token的形式解决前后端缓存问题及缓存一致性问题。具体可以看我之前写的:JSON WEB TOKEN解决跨域、缓存一致性问题
首先简单介绍下shiro的关键概念
Subject:用户主体(把操作交给SecurityManager)
SecurityManager:安全管理器(关联Realm)
Realm: Shiro连接数据的桥梁
配置pom文件:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
弄一个包专门写个shiro的配置类,这里新建一个com.hykj.fiserver.env.shiro:
@Configuration
public class Config {
/**
* 常用的过滤器:
* anon:无需认证就能访问
* authc:必须认证了才能访问 user:必须拥有记住我功能才能访问
* perms:拥有对每个资源的权限才能访问
* role:拥有某个角色权限才能访问
* @return
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();// 添加一个拦截器
filterMap.put("/loginAction/login", "anon");//登录的话无需认证就能访问
filterMap.put("/**", "authc");//拦截所有请求,有认证才能登录
//这里没有启用授权过滤器,就是写个例子
//filterMap.put("/user/add", "perms[user:add]");
bean.setFilterChainDefinitionMap(filterMap);
// 设置登录请求,这里是被拦截后回转的页面
bean.setLoginUrl("/loginAction/loginError");
// 设置未授权页面,这里是未授权跳转的页面,没有启动
bean.setUnauthorizedUrl("/loginAction/noauth");
return bean;
}
/**
* 安全管理器
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setSessionManager(sessionManager());
securityManager.setRealm(userRealm());
return securityManager;
}
@Bean
public SessionManager sessionManager() {
TokenSessionManager tokenSessionManager = new TokenSessionManager();
return tokenSessionManager;
}
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
}
接下来写UserRealm
public class UserRealm extends AuthorizingRealm {
private final static Log _logger = LogFactory.getLog(UserRealm.class);
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//这里是权限认证的地方,我项目中没有使用shiro的权限认证
System.out.println("执行授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// info.addStringPermission("user:add");
// 拿到当前登录的用户
Subject subject = SecurityUtils.getSubject();
UserCache currentUser = (UserCache) subject.getPrincipal();
info.addStringPermission(currentUser.getOpId());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//subject.login(token);一定会到这里来,这里就是验证登录的地方
UserCache user = new UserCache();
user.setOpId("test");
user.setUserPassword("1234");
user.setUserName("test");
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//这里要判断用户名和密码了,通常是去数据库核对用户名和密码了
if(!new String(userToken.getUsername()).equals(user.getUserName())) {
//用户名错误
throw new UnknownAccountException();
}
if (!new String(userToken.getPassword()).equals(user.getUserPassword())) {
//密码错误
throw new IncorrectCredentialsException();
}
return new SimpleAuthenticationInfo(user, user.getUserPassword(), "");
}
}
token就是配置在这里了,要求前端在头中加入token
public class TokenSessionManager extends DefaultWebSessionManager {
private static final String TOKEN = "token";
private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
private final static Log _logger = LogFactory.getLog(TokenSessionManager.class);
public TokenSessionManager() {
super();
}
@Override
public Serializable getSessionId(ServletRequest request, ServletResponse response) {
//
String token = WebUtils.toHttp(request).getHeader(TOKEN);
// 前端请求头必须传入token的值,把他的值当做是sessionId
if (!StringUtils.isEmpty(token)) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
_logger.info("token=" + token);
return token;
} else {
_logger.info("token为空");
return null;
// 否则按默认规则从cookie取sessionId
//return super.getSessionId(request, response);
}
}
}
接下来进行测试,注意以下几点:
1.UserRealm做的user 其id为test,密码为1234,要都匹配才能过
2.登录失败是返回/loginAction/loginError,该接口返回一个字符串,如下代码:
@RequestMapping(value = "/loginError", produces = "application/json; charset=utf-8")
@ResponseBody
public String loginError(@RequestHeader("token") String token) {
return "hello world loginError; token = " + token;
}
3./loginAction/login接口是不会被拦截的,其他请求会被拦截
测试工具:apipost
测试地址1:http://localhost:13001/FIServer/loginAction/userTest
@RequestMapping(value = "/userTest", produces = "application/json; charset=utf-8")
@ResponseBody
public String userTest() {
return "hello world Test";
}
因为不是loginAction/login所以被拦截了,虽然有token但还是被转发到loginError
测试地址2:http://localhost:13001/FIServer/loginAction/login?username=testname&password=12345
@RequestMapping(value = "/login", produces = "application/json; charset=utf-8")
@ResponseBody
public String login(String username, String password, HttpServletRequest request) {
// 获取当前的用户
Subject subject = SecurityUtils.getSubject();
// 封装用户的登录数据
UsernamePasswordToken userToken = new UsernamePasswordToken(username, password);
try {
subject.login(userToken);
UserCache user = (UserCache) subject.getPrincipal();
System.out.println(user.getOpId());
String token = _jwtTokenUtil.createToken(getJSON(user));
return "登录成功token=" + token;
} catch (UnknownAccountException e) {
return "用户名错误";
} catch (IncorrectCredentialsException e) {
return "密码错误";
}
}
很明显因为密码不是1234所以一定会返回用户名错误
测试地址3:http://localhost:13001/FIServer/loginAction/login?username=test&password=1234
这次就没有问题啦,注意登录的时候是没有token的,并且还会生产token返回给前端,就是图中看不懂的那一堆字符
最后
以上就是幸福大碗为你收集整理的SpringCloud+shiro+前后端分离的全部内容,希望文章能够帮你解决SpringCloud+shiro+前后端分离所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复