shiro的无状态web集成。所谓无状态就是服务器端无状态,就是不保存会话。一般的会话机制的web应用,都是session机制来保存用户状态。无状态的web应用就是每次请求都带上相应的用户名进行登录。
具体的实践就是:客户端传入秘钥和一个消息作为输入,他们声称相应消息摘要,秘钥是只有客户端和服务端知道的。访问的时候服务端对消息摘要进行验证。
具体的实例如下:
首先我们创建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。
这样就可以使用啦。这里没有列出对用户名和消息生成消息摘要的类,基本上就是对属性加密的一个类。
时间: 2024-10-21 04:58:39