shiro的无状态web集成。所谓无状态就是服务器端无状态,就是不保存会话。一般的会话机制的web应用,都是session机制来保存用户状态。无状态的web应用就是每次请求都带上相应的用户名进行登录。
具体的实践就是:客户端传入秘钥和一个消息作为输入,他们声称相应消息摘要,秘钥是只有客户端和服务端知道的。访问的时候服务端对消息摘要进行验证。
具体的实例如下:
首先我们创建subject的工厂必须是不保存session的:
具体的实践就是:客户端传入秘钥和一个消息作为输入,他们声称相应消息摘要,秘钥是只有客户端和服务端知道的。访问的时候服务端对消息摘要进行验证。
具体的实例如下:
首先我们创建subject的工厂必须是不保存session的:
public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {
@Override
public Subject createSubject(SubjectContext context)
{
context.setSessionCreationEnabled(false);
return super.createSubject(context);
}
}
我们自定义一个无状态的Filter:
public class StatelessAuthcFilter extends AccessControlFilter {
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
//客户端生成的消息摘要
String clientDigest=servletRequest.getParameter("digest");
//客户端传入的用户身份
String username=servletRequest.getParameter("username");
//客户端的参数列表
String param1=servletRequest.getParameter("param1");
String param2=servletRequest.getParameter("param2");
Map<String,String> params=new HashMap<>();
params.put("param1",param1);
params.put("param2",param2);
//生成无状态Token
StatelessToken token=new StatelessToken(username,params,clientDigest);
try {
getSubject(servletRequest,servletResponse).login(token);
}
catch (Exception e) {
e.printStackTrace();
onLoginFail(servletResponse);
}
return false;
}
private void onLoginFail(ServletResponse response) throws IOException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.getWriter().write("login error");
}
}
shiro框架没有提供专门的无状态的token,我们自己定义一个:
public class StatelessToken implements AuthenticationToken {
private String username;
private Map<String, ?> params;
private String clientDigest;
public StatelessToken(String username, Map<String, ?> params, String clientDigest) {
this.username = username;
this.params = params;
this.clientDigest = clientDigest;
}。。。。。此处省略set和get代码
自定义无状态的realm:
public class StatelessRealm extends AuthorizingRealm {
@Autowired
private IMememberService memberService;
@Override
public boolean supports(AuthenticationToken token) {
//仅支持StatelessToken类型的Token
return token instanceof StatelessToken;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) principalCollection.getPrimaryPrincipal();
Member user = memberService.findByUsername(username);
// System.out.println(user);
if (user != null) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
return authorizationInfo;
} else throw new IncorrectCredentialsException();
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
StatelessToken statelessToken = (StatelessToken) token;
String username = statelessToken.getUsername();
String key = getKey(username);//根据用户名获取密钥(和客户端的一样)
//在服务器端生成客户端参数消息摘要
String serverDigest = HmacSHA256Utils.digest(key, statelessToken.getParams());
System.out.println(statelessToken.getClientDigest());
System.out.println(serverDigest);
//然后进行客户端消息摘要和服务器端消息摘要的匹配
return new SimpleAuthenticationInfo(
username,
serverDigest,
getName());
}
/**
* 获取秘钥,此处是硬编码的一个
*
* @param username
* @return
*/
private String getKey(String username) {
if("admin".equals(username))
{
return "dadadswdewq2ewdwqdwadsadasd";
}
return null;
}
}
然后进行shiro的配置文件部分内容:
<!--statelessReealm-->
<bean id="statelessRealm" class="com.supuy.sps.realm.StatelessRealm">
<property name="cachingEnabled" value="false"/>
</bean>
<!--statelessReealm subject工厂-->
<bean id="subjectFactory" class="com.supuy.sps.realm.factory.StatelessDefaultSubjectFactory"></bean>
<!--statelessFilter-->
<bean id="statelessFilter" class="com.supuy.sps.realm.filter.StatelessAuthcFilter"></bean>
这里的sessionManager的sessionValidationSchedulerEnabled属性一定得设置为false。
这样就可以使用啦。这里没有列出对用户名和消息生成消息摘要的类,基本上就是对属性加密的一个类。