java认证框架_easy-security

这是一个Java的安全认证框架,实现登录,权限认证功能。脱离容器的会话ID,轻松实现负载均衡而不用担心会话丢失。支持多种会话缓存实现,权限控制灵活,与spring框架配置使用简单。

获取用户登录信息

SessionManager sm = ...;

Session session = sm.getSession();

if(session.isLogin()) {

String userId = session.getUserId();

}

sm.getSession()可以获取到用户的当前会话,从会话中你可以获取访问凭证,用户ID等信息,该方法不会返回Null(我们下面会说到如何获取到SessionManager)。

用户登录

@Autowire

private SessionManager sm;

@PostMapping("login")

String login(String username, String password) {

UsernamePasswordAuthentication auth = new UsernamePasswordAuthentication(username, password);

SessionWrapper wrapper = sm.login(auth);

Session session = wrapper.getSession();

Map data = wrapper.getExtras();//我们自己添加的额外信息

String token = session.getAccessToken().toString();

return token;

}

我们使用框架自带的UsernamePasswordAuthentication认证类来传递账号和密码进行登录,如果你希望传递更多的信息到“登录处理器”中,则需要继承该类或实现Authentication接口,比如你需要区别后台用户和会员的用户类型。

登录成功后,我们得到一个包装类,你可以获取到访问凭证token,然后将这个token返回给客户端,客户端请求“受保护资源”的时候,需要带上这个token,否则会视为无权限。

配置

在使用框架前,我们需要做一些配置,比如处理登录操作的服务类,权限数据源,登录信息缓存等。下面我们来一个个看怎么配置这些东西,以下的代码我们是基于Spring Framework框架的。

登录处理器

创建一个类实现LoginHandler接口,该接口只有一个登录方法。

public class LoginService implements LoginHandler {

@Override

public LoginResult login(Authentication auth) {

//转成什么类,取决你使用SessionManager#login时传递什么东西

UsernamePasswordAuthentication up = (UsernamePasswordAuthentication)auth;

String username = up.getUsername();

String password = up.getPassword();

//查询数据库,判断用户是否存在

//...

String userId = ...;

Set resourceId = ...;

LoginResult result = new LoginResult(userId, resourceId);

return result;

}

}

处理器返回一个LoginResult对象,你需要将用户的ID和用户拥有的权限(资源)ID放到LoginResult中。

缓存

缓存是用来存储用户登录后的信息,比如用户ID,用户拥有的权限信息,其他扩展信息等,因为每次请求我们都需要去检索这些信息来做验证,所以这些信息应该放在一个可快速读取到的地方,如内存,Redis等。我们内置了一些缓存实现,你可以直接拿来用。

FileSessionCache

一般用于开发阶段,在开发时,我们经常需要重启服务,登录信息保存在文件中的话,重启后也不会导致会话消失。

MapSessionCache

基于HashMap实现的内存型缓存,适用于小项目,用户基础不大的项目。

JedisSessionCache

基于Redis实现的。

SpringRedisSessionCache

也是基于Redis实现,与JedisSessionCache的区别是,JedisSessionCache需要你自己提供Redis的实现(比如JedisPool),而SpringRedisSessionCache使用你项目中已经定义的缓存实现(如果你项目有其他地方也用到缓存)。

如果上面的缓存无法满足你的需求,你可以自己实现,只需要实现SessionCache接口即可,下面我们就来看看怎么定义缓存。首先创建一个spring的配置类:

@Configuration

public class SecurityConfig {

}

在里面定义我们的Bean。

//此处定义是在SecurityConfig里面的

@Bean

public SessionCache sessionCache() {

return new MapSessionCache();

}

SessionManager

SessionManager是一个使用最频繁的类,当配置都设置好后,在应用程序中你需要获取用户会话信息时,都需要从该类中获取。

@Bean

public SessionManager sessionManager(LoginHandler loginHandler, SessionCache cache) {

//loginHandler和cache我们前面已经定义了

SessionManager manager = new SessionManager(loginHandler, cache);

//我们返回给客户端的访问凭证是使用用户ID等信息加密生成的,你需要提供一个密钥

manager.setAccessTokenSecretKey("my-secret-key");

return manager;

}

//为SessionManager绑定请求事件,主要用户访问凭证的解析,以及在其他地方能够获取到ServletRequest对象

@Bean

public ServletListenerRegistrationBean registerRequestHodler(SessionManager sessionManager){

ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();

bean.setListener(sessionManager);

return bean;

}

权限

也就是保护资源,比较常见的保护资源有URL,我们需要定义一个数据源来告诉验证器哪些资源(URL)是受保护的。

@Bean

public UriResourceDataSource uriResourceDataSource() {

//我们定义了两个URL资源,如果用户访问这些URL时,没有对应的权限ID,就会被拒绝。

//URL匹配规则请参考spring的AntPathMatcher

UriResourceDataSource ds = new UriResourceDataSource();

ds.addResource(SimpleUriResource.of("001", "/admin/**"));

ds.addResource(SimpleUriResource.of("002", "/member"));

return ds;

}

验证器

我们上面定义了很多对象,目的是为了在验证器中使用这些对象来做拦截验证,通常验证的规则如下:

拦截Http请求。

获取URL信息,然后从“数据源”中查找一个URL资源,如果找到,表示这个请求是受保护的资源。

获取用户会话。

将保护资源和会话交给验证程序。

我们提供了一个验证工具HttpSecurityService,通常你需要根据你的应用环境自己写一个拦截器做验证,目前我们只提供spring框架的拦截器,如果你是用spring框架,则可以直接使用我们的验证器。

@Bean

public SecurityValidationInterceptor securityInterceptor() {

SecurityValidationInterceptor interceptor =

new SecurityValidationInterceptor();

return interceptor;

}

验证失败的处理

默认的,验证失败后会返回401状态码,如果你希望自己处理验证失败的结果,实现SecurityValidationListener接口。

public class SecurityFailListener implements SecurityValidationListener {

@Override

public void onFail(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, HttpSecurityService.ValidationResult r) {

//在这里处理验证错误结果,你可以从ValidationResult中获取失败的原因

//然后使用httpServletResponse返回消息

}

}

//然后加到SecurityValidationInterceptor中

interceptor.setOnSecurityValidationFail(new SecurityFailListener());

至此,验证框架所需的东西都已经准备就绪。

使用访问凭证发送请求

用户登录成功后,你可以得到访问凭证session.getAccessToken().toString(),并返回给客户端,客户端每次发送请求都带上这个凭证。

http://domain/path?_token=your_token

或者你可以放在请求头中:

X-User-Token: your_token

如果你希望修改凭证参数名称或请求头名称,可以通过AccessTokenReader类修改。

AccessTokenReader.setTokenQueryParamName("token_name");

AccessTokenReader.setTokenHeaderName("token_head_name");

进阶

过期时间设置

会话过期时间)

会话是存储在缓存中的,每次访问时,会话的过期时间都会被更新,你可以使用SessionManager#setSessionTimeout来设置会话的过期时间,默认为两个小时。

凭证过期时间)

凭证的过期时间被加密在凭证自身里面,凭证的过期时间是不会更新的,这是为了防止凭证被盗用后,可以一直无限使用,当凭证过期了后,那么就会被丢弃。凭证的过期时间默认为24小时,可以使用SessionManager#setAccessTokenTimeout修改。

会话过期和凭证过期的区别就是,会话过期能够被更新,凭证不能,当凭证过期后,会话自然无法再访问,比如凭证过期为24小时,会话为1分钟,如果用户每隔30秒发送请求,那么会话能被使用24小时。如果凭证过期为1小时,会话过期为2小时,那么1小时后会话也会无法访问。

将额外的信息混合到凭证中

默认,凭证信息由一个UUID和过期时间生成,如果你希望加入一些额外信息进去参加生成,可以这么做:

//在登录处理器中

LoginResult result = ...;

result.setTokenMergeInfo(user::getName);

return result;

setTokenMergeInfo方法接收一个TokenMergeInfo接口。当你希望获取这个扩展信息时,可以通过Session获取。

Session s = sessionManager.getSession();

String extra = s.getAccessToken().getExtra();

这个扩展信息是不在缓存中的,而是在凭证字符串中的。

修改访问凭证规则

有时候需要为不同的用户生成不同的访问凭证,比如后台管理员和前台注册会员使用不同的凭证串。

@Bean

public SessionManager sessionManager(LoginHandler h, SessionCache c) {

SessionManager manager = new SessionManager(h, c);

manager.setAccessTokenSecretKey("your_secret_key");

// 根据前台和后台的用户生成不同前缀的token

// SecurityAuthentication是自定义类,继承UsernamePasswordAuthentication

manager.setOnAccessTokenBuildListener((r, a, token) -> {

int from = ((SecurityAuthentication)a).getFrom();

String prefix;

if(from == SecurityAuthentication.FROM_FRONTEND) {

prefix = USER_TOKEN_PREFIX_FRONT;

} else {

prefix = USER_TOKEN_PREFIX_BACKEND;

}

return prefix + ":" + token;

});

//当准备解析凭证的时候,你需要还原你修改前的凭证(也就是去掉prefix)

manager.setOnAccessTokenResolveListener(t -> {

String[] tmp = t.split(":");

if(tmp.length == 2) {

RequestContextHolder.getRequestAttributes().setAttribute("user_type", tmp[0], SCOPE_REQUEST);

return tmp[1];

} else {

return t;

}

});

return manager;

}

验证器注解

如果你是使用SecurityValidationInterceptor这个验证器,那么你可以为你的Controller方法加上这两个注解。

@PostMapping("login")

@Anonymous//标识这个方法是非保护资源,不用登录也可以访问

void login() {

}

@GetMapping("user/info")

@OnlyLogin//标识这个方法为只要登录即可方法,不用拥有对应的权限

String getUserName() {

}

这两个注解可以放在方法上,也可以放在类上,放在类上的话表示这个类的所有方法都有效,只有方法被标注了XXXMapping才有效果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值