SpringBoot集成Shiro\JWT
程序员文章站
2024-03-20 20:18:16
...
1.大体思路总结
shiro:权限验证
(1)需要自定义realm,realm中处理用户角色权限相关的东西.
(2)shiroConfig.java,shiro配置i相关
(3)loginController,登录处理
(4)拦截器,集成jwt后,对token进行校验
JWT:生成token的工具
(1)工具类:生成token,验证token等各种方法
(2)JWTToken.java,一个存token的类
2.上代码
(1)maven配置
<!-- jwtToken -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
(2)shiroConfig
package com.lx.config;
import com.lx.filter.JwtTokenFilter;
import com.lx.shiro.CustomRealm;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class shiroConfig {
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
//将自己的验证方式加入容器
@Bean
public CustomRealm myShiroRealm() {
CustomRealm customRealm = new CustomRealm();
return customRealm;
}
//权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
@Bean
public FilterRegistrationBean delegatingFilterProxy(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
DelegatingFilterProxy proxy = new DelegatingFilterProxy();
proxy.setTargetFilterLifecycle(true);
proxy.setTargetBeanName("shiroFilterFactoryBean");
filterRegistrationBean.setFilter(proxy);
return filterRegistrationBean;
}
//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {//, JwtTokenFilter filter
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//登录
shiroFilterFactoryBean.setLoginUrl("/login");
//首页
shiroFilterFactoryBean.setSuccessUrl("/index");
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
Map<String, javax.servlet.Filter> mapFilter = new HashMap<String, Filter>();
mapFilter.put("jwtFilter", new JwtTokenFilter());
shiroFilterFactoryBean.setFilters(mapFilter);
Map<String, String> map = new LinkedHashMap<String, String>();
//登出
map.put("/logout", "logout");
//对所有用户认证
// map.put("/**", "authc");
map.put("/login","anon");
//拦截器
map.put("/**", "jwtFilter");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//加入注解的使用,不加入这个注解不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
(3)realm
loginService.getUserByName是去数据库里查用户信息的
package com.lx.shiro;
import com.lx.bean.Permissions;
import com.lx.bean.Role;
import com.lx.bean.User;
import com.lx.config.JWTToken;
import com.lx.dao.User2;
import com.lx.service.LoginService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CustomRealm extends AuthorizingRealm {
@Autowired
private LoginService loginService;
// @Override
// public boolean supports(AuthenticationToken token) {
// return token instanceof SimpleUsernameRealm;
// }
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取登录用户名
String name = (String) principalCollection.getPrimaryPrincipal();
//根据用户名去数据库查询用户信息
User2 user = loginService.getUserByName(name);
//添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (Role role : user.getRoles2()) {
//添加角色
simpleAuthorizationInfo.addRole(role.getRoleName());
//添加权限
for (Permissions permissions : role.getPermissions()) {
simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
}
}
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//加这一步的目的是在Post请求的时候会先进认证,然后在到请求
if (authenticationToken.getPrincipal() == null) {
return null;
}
//获取用户信息
String name = authenticationToken.getPrincipal().toString();
User2 user = loginService.getUserByName(name);
if (user == null) {
//这里返回后会报出对应异常
return null;
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
String name1 = getName();
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), ByteSource.Util.bytes("salt"), name1);
return simpleAuthenticationInfo;
}
}
}
(4)拦截器
package com.lx.filter;
import com.lx.utils.JWTUtil;
import com.sun.deploy.util.StringUtils;
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import tk.mybatis.mapper.util.StringUtil;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
/**
* @author: liuxu
* @create: 2020-04-12 21:04
*/
@Component("jwtTokenFilter")
public class JwtTokenFilter extends FormAuthenticationFilter {
/**
* 如果时登录请求,返回true
* @param servletRequest
* @param servletResponse
* @return
* @throws Exception
*/
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
if (isLoginRequest(servletRequest, servletResponse)) {
if (isLoginSubmission(servletRequest, servletResponse)) {
return true;
}
}else {
throw new RuntimeException("token不存在,非法请求");
}
return false;
}
/**
* 所有请求都会被拦截,当返回false时会调用onAccessDenied
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Subject subject = this.getSubject(request, response);
HttpServletRequest request1 = (HttpServletRequest) request;
String token = request1.getHeader("X-Auth-Token");
if (StringUtil.isEmpty(token)) {
return false;
}
String[] perms = (String[]) mappedValue;
boolean isPermitted = true;
if (perms != null && perms.length > 0) {
}
boolean authenticated = subject.isAuthenticated();
return authenticated;
}
}
(5)登录测试
package com.lx.controller;
import com.lx.bean.User;
import com.lx.dao.User2;
import com.lx.service.LoginService;
import com.lx.utils.JWTUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class LoginController {
@Autowired
private LoginService loginService;
@RequestMapping("/login")
public String login(@RequestBody User user) {
//添加用户认证信息
Subject subject = SecurityUtils.getSubject();
String token = "";
try{
token = JWTUtil.getToken(user.getUserName());
System.out.println(token);
}catch (Exception e ){
e.printStackTrace();
}
String salt = "salt";
Md5Hash md5Hash1 = new Md5Hash("1234");
Md5Hash md5Hash2 = new Md5Hash("1234",salt);
Md5Hash md5Hash3 = new Md5Hash("1234",salt, 1);
Md5Hash md5Hash4 = new Md5Hash("1234",salt, 2);
System.out.println(md5Hash1);
System.out.println(md5Hash2);
System.out.println(md5Hash3);
System.out.println(md5Hash4);
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
user.getUserName(),
md5Hash4.toString());
try {
//进行验证,这里可以捕获异常,然后返回对应信息
subject.login(usernamePasswordToken);
boolean admin = subject.isPermitted("add");
// subject.checkRole("admin");
// subject.checkPermissions("query", "add");
System.currentTimeMillis();
} catch (AuthenticationException e) {
e.printStackTrace();
return "账号或密码错误!";
} catch (AuthorizationException e) {
e.printStackTrace();
return "没有权限";
}
return token;
}
//注解验角色和权限
@RequiresRoles("admin")
@RequiresPermissions("add")
@RequestMapping("/index")
public String index() {
return "index!";
}
// @RequiresRoles("admin")
// @RequiresPermissions("add")
@RequestMapping("/aaa")
public String aaa() {
List<User2> user = loginService.getUser();
return "index!"+user.size();
}
}
2.JWT
(1)JWTToken
package com.lx.config;
import org.apache.shiro.authc.AuthenticationToken;
/**
* @author: liuxu
* @create: 2020-04-11 21:30
*/
public class JWTToken implements AuthenticationToken {
private static final long serialVersionUID = 1L;
// 秘钥
private String token;
public JWTToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}
(2)jwt工具类
package com.lx.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author: liuxu
* @create: 2020-04-11 21:32
*/
@Component
public class JWTUtil {
private static final long EXPIRATIONTIME = 604000000;
private static final String SECRET = "ad5608bs32kzfwd48qfwgps157";
private static final String TOKEN_PREFIX = "Bearer";
private static final String HEADER_STRING = "Authorization";
/**
* 获取当前时间
* @return
*/
public static Date getCurrentDate() {
return new Date(System.currentTimeMillis());
}
/**
* 通过claims获取token中的到期时间
* @param token
* @return
*/
public static Date getExpiration(String token) {
Claims claims = JWTUtil.getClaim(token);
if (claims == null) {
return null;
} else {
return claims.getExpiration();
}
}
/**
* 通过claims获取token中的登录名
* @param token
* @return
*/
public static String getUsername(String token) {
Claims claims = JWTUtil.getClaim(token);
if (claims == null) {
return null;
} else {
return claims.get("username").toString();
}
}
/**
* 获取claims
* @param token
* @return
*/
public static Claims getClaim(String token) {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
}
/**
* token校验
* @param token
* @param userName
* @return
*/
public static boolean verify(String token, String userName) {
return userName.equals(JWTUtil.getUsername(token)) &&
getExpiration(token).before(JWTUtil.getCurrentDate());
}
/**
* 登录时生成token
* @param username
* @return
* @throws UnsupportedEncodingException
*/
public static String getToken(String username) throws UnsupportedEncodingException {
Map<String, Object> claims = new HashMap<String, Object>(1);
claims.put("username", username);
String token = Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
// 附带username信息
return token;
}
}
大体上就这么些吧~。过两天再试试注解权限验证
上一篇: Singleton pattern
下一篇: PHP数据库保存session会话
推荐阅读
-
Springboot+jwt+shiro
-
SpringBoot集成Shiro\JWT
-
CAS单点登录-客户端集成(shiro、springboot、jwt、pac4j)(十)
-
Shiro+JWT 前后端分离方案
-
Springboot+SpringSecurity+JWT +Vue实现前后端分离的登录 权限管理以及token管理
-
SpringBoot集成Security,前后端分离的SecurityConfig配置
-
前后端分离:SpringBoot + Jwt 登录验证token
-
springboot+maven 集成lombok简化代码
-
SpringBoot集成lombok简化代码
-
springboot集成easyExcel导入excel