shiro整合jwt
首先肯定是导包
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency>
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency>
|
然后就是shiro的配置了,jwt工具类就不多说了
ShiroConfig
Realm
- Realm是授权和验证的核心逻辑类
- 这里要准备一个PassWordRealm和JWTRealm,分别用来验证密码登录和jwt登录
@Slf4j public class PassWordRealm extends AuthorizingRealm { @Override public boolean supports(AuthenticationToken token) { return token instanceof UsernamePasswordToken; }
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
return simpleAuthorizationInfo; }
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken; String username = usernamePasswordToken.getUsername(); User user = new User("UserName","PassWord"); if(user == null){ throw new AuthenticationException("用户名错误"); }
return new SimpleAuthenticationInfo(user,user.getPassword(),getName()); } }
|
@Slf4j public class JwtRealm extends AuthorizingRealm {
@Autowired private UserService userService;
@Override public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken; }
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
return simpleAuthorizationInfo; }
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String token = authenticationToken.getCredentials().toString(); String username = JwtUtil.getUsername(token);
User user = new User(); if(user == null){ throw new AuthenticationException("用户不存在"); }
if (!JwtUtil.verify(token)) { throw new AuthenticationException("认证异常"); }
return new SimpleAuthenticationInfo(token, token,getName()); } }
|
JwtToken
public class JwtToken implements AuthenticationToken {
private String jwtToken;
public JwtToken(String jwtToken) { this.jwtToken = jwtToken; } @Override public Object getPrincipal() { return jwtToken; }
@Override public Object getCredentials() { return jwtToken; } }
|
这里说一下上面的校验规则:
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { Object tokenCredentials = getCredentials(token); Object accountCredentials = getCredentials(info); return equals(tokenCredentials, accountCredentials); }
|
JwtFilter
这个类是自定义的拦截规则,在下面的ShiroConfig里面会具体讲
public class JwtFilter extends FormAuthenticationFilter {
@Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request; Cookie[] cookies = httpServletRequest.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if(cookie.getName().contains("Token")){ try { getSubject(request,response).login(new JwtToken(cookie.getValue())); return true; } catch (AuthenticationException e) { return false; } } } }
return false; } }
|
UserModularRealmAuthenticator
public class UserModularRealmAuthenticator extends ModularRealmAuthenticator {
@Override protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
Collection<Realm> realms = getRealms(); Collection<Realm> typeRealm = new ArrayList<>();
try { JwtToken jwtToken = (JwtToken) authenticationToken;
for (Realm realm : realms) { if(realm.getName().contains("Jwt")){ typeRealm.add(realm); } } return doSingleRealmAuthentication(typeRealm.iterator().next(), jwtToken);
} catch (ClassCastException e) {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken; for (Realm realm : realms) { if(realm.getName().contains("PassWord")){ typeRealm.add(realm); } } if(typeRealm.size() == 1){ return doSingleRealmAuthentication(typeRealm.iterator().next(), usernamePasswordToken); }else { return doMultiRealmAuthentication(typeRealm, usernamePasswordToken); }
}
} }
|
ShiroConfig
@Configuration public class ShiroConfig {
@Bean public Realm JwtRealm(){ return new JwtRealm(); } @Bean public Realm PassWordRealm(){ return new PassWordRealm(); } @Bean public UserModularRealmAuthenticator UserModularRealmAuthenticator(){
UserModularRealmAuthenticator userModularRealmAuthenticator = new UserModularRealmAuthenticator(); userModularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy()); return userModularRealmAuthenticator; }
@Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/register","anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/js/**/*","anon"); filterChainDefinitionMap.put("/css/**/*","anon"); filterChainDefinitionMap.put("/icon/**/*","anon"); filterChainDefinitionMap.put("/img/**/*","anon"); filterChainDefinitionMap.put("/fonts/**/*","anon"); filterChainDefinitionMap.put("/**", "jwt");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
shiroFilterFactoryBean.setLoginUrl("/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/index");
HashMap<String, Filter> objectObjectHashMap = new HashMap<>(1); objectObjectHashMap.put("jwt", new JwtFilter());
shiroFilterFactoryBean.setFilters(objectObjectHashMap);
return shiroFilterFactoryBean; }
@Bean public DefaultWebSecurityManager defaultWebSecurityManager( @Qualifier("JwtRealm") Realm jwtRealm, @Qualifier("PassWordRealm") Realm passWordRealm, @Qualifier("UserModularRealmAuthenticator") UserModularRealmAuthenticator userModularRealmAuthenticator){ DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setAuthenticator(userModularRealmAuthenticator);
List<Realm> list = new ArrayList<>(); list.add(jwtRealm); list.add(passWordRealm); defaultWebSecurityManager.setRealms(list);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); defaultSessionStorageEvaluator.setSessionStorageEnabled(false); subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); defaultWebSecurityManager.setSubjectDAO(subjectDAO);
return defaultWebSecurityManager; }
|
到此,整合就已经全部结束了,最后写一点demo进行一下验证就行了
Controller
@PostMapping("/login") @ResponseBody public String login(User user, HttpServletResponse response){ Subject subject = SecurityUtils.getSubject(); try { subject.login(new UsernamePasswordToken(user.getUsername(), user.getPassword())); response.addCookie(new Cookie("Token",JwtUtil.sign(user.getUsername()))); return "登录成功"; }catch (IncorrectCredentialsException e){ return "密码错误"; }catch (AuthenticationException e) { return e.getMessage(); } }
@RequestMapping({"/index","/"}) public String index(){ if (SecurityUtils.getSubject().isAuthenticated()) { return "success"; } return "index"; }
@RequestMapping("/logOut") public String logOut(HttpServletResponse httpServletResponse){ Cookie cookie = new Cookie("Token",""); cookie.setMaxAge(0); httpServletResponse.addCookie(cookie); SecurityUtils.getSubject().logout(); return "index"; }
|
Bean.User
@Data @AllArgsConstructor @NoArgsConstructor @TableName("user") public class User { private Integer id; @NotNull @NotEmpty private String username; @NotNull @NotEmpty private String password;
}
|
index页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="登录"> </form> </body> </html>
|
demo到这里就结束了,如有疑问,可以在评论区探讨,有不同意见请轻喷( •̀ ω •́ )✧