具體的實踐就是:客戶端傳入祕鑰和一個消息作爲輸入,他們聲稱相應消息摘要,祕鑰是隻有客戶端和服務端知道的。訪問的時候服務端對消息摘要進行驗證。
具體的實例如下:
首先我們創建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。
這樣就可以使用啦。這裏沒有列出對用戶名和消息生成消息摘要的類,基本上就是對屬性加密的一個類。