# 1.介绍 ## 1.shiro
- Apache Shiro是一个 java 的安全(权限)框架。
- Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE环境,也可以用在javaEE环境。
- Shiro可以完成,认证、授权、加密、会话管理、Web集成、缓存等。
2.jwt
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。
我们利用一定的编码生成 Token,并在 Token 中加入一些非敏感信息,将其传递。
2.使用
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.2.0</version>
</dependency>
2.编写代码
1.ShiroConfig
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("jwt", new JwtFilter());
bean.setFilters(filterMap);
bean.setSecurityManager(defaultWebSecurityManager);
bean.setUnauthorizedUrl("/unauthorized");
Map<String, String> filterRuleMap = new HashMap<>();
filterRuleMap.put("/**", "jwt");
filterRuleMap.put("/unauthorized", "anon");
filterRuleMap.put("/sys-user/login", "anon");
filterRuleMap.put("/captcha", "anon");
bean.setFilterChainDefinitionMap(filterRuleMap);
return bean;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("UserRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean(name = "UserRealm")
public UserRealm userRealm(){
return new UserRealm();
}
}
2.UserRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
SysUserServiceImpl sysUserService;
@Autowired
SysRoleServiceImpl sysRoleService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
Subject subject = SecurityUtils.getSubject();
SysUser sysUser = (SysUser)subject.getPrincipal();
System.out.println("当前授权用户信息:" + sysUser);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
ArrayList<SysRole> userRoles = sysUserService.getUserRoles(sysUser.getId());
System.out.println("当前用户的角色列表:" + userRoles);
Set<String> userRoleSet = new HashSet<>();
userRoles.forEach(userRole -> userRoleSet.add(userRole.getCode()));
ArrayList<Long> longRoles = new ArrayList<>();
userRoles.forEach(userRole -> longRoles.add(userRole.getId()));
ArrayList<SysMenu> userPermission = sysRoleService.getUserPermission(longRoles);
System.out.println("当前用户的角色权限列表:" + userPermission);
Set<String> userPermissionSet = new HashSet<>();
userPermission.forEach(SysMenu -> userRoleSet.add(SysMenu.getPerms()));
info.setRoles(userRoleSet);
info.setStringPermissions(userPermissionSet);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String token = (String) authenticationToken.getCredentials();
String username = JwtUtil.getUsername(token);
if (username == null || !JwtUtil.verify(token, username)) {
throw new AuthenticationException("token认证失败!");
}
SysUser sysUser = sysUserService.getUserByName(username);
if (sysUser == null) {
throw new AuthenticationException("用户不存在");
}
System.out.println("登录成功");
return new SimpleAuthenticationInfo(sysUser, token, "myRealm");
}
}
3.JwtFilter
@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (((HttpServletRequest) request).getHeader("token") != null) {
try {
executeLogin(request, response);
return true;
} catch (AuthenticationException e) {
responseError(response);
}
}
return true;
}
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader("Token");
JwtToken jwtToken = new JwtToken(token);
getSubject(request, response).login(jwtToken);
return true;
}
private void responseError(ServletResponse response) {
try {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.sendRedirect("/unauthorized");
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
4.Controller
@Autowired
private Producer producer;
@GetMapping("/captcha")
public Result captcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
String code = producer.createText();
String key = UUID.randomUUID().toString();
BufferedImage image = producer.createImage(code);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", outputStream);
BASE64Encoder encoder = new BASE64Encoder();
String str = "data:image/jpeg;base64,";
String base64Img = str + encoder.encode(outputStream.toByteArray());
redisUtil.hset(Const.CAPTCHA_KEY, key, code, 120);
log.info("验证码 -- {} -- {}", key, code);
return Result.succ(
MapUtil.builder()
.put("token", key)
.put("base64Img", base64Img)
.build()
);
}
@PostMapping("/login")
public Result login(@RequestBody JSONObject jsonObject, HttpServletRequest request, HttpServletResponse response){
System.out.println();
String codeToken = (String) jsonObject.get("token");
String code = (String) jsonObject.get("code");
if (codeToken == null || code == null) {
return Result.succ(400, "验证码不能为空", null);
}
if (!code.equals(redisUtil.hget(Const.CAPTCHA_KEY, codeToken))) {
return Result.succ(400, "验证码错误", null);
}
String username = (String) jsonObject.get("username");
String password = (String) jsonObject.get("password");
SysUser user = sysUserService.getUserByName(username);
if(user == null) {
return Result.succ(400, "用户不存在", null);
}
if(!user.getPassword().equals(password)) {
return Result.succ(400, "密码错误", null);
}
redisUtil.hdel(Const.CAPTCHA_KEY, codeToken);
response.setHeader("Token", JwtUtil.createToken(username));
return Result.succ(200, "登录成功", "");
}
5.JwtUtil
public class JwtUtil {
private static final long EXPIRE_TIME = 60 * 24 * 60 * 1000;
private static final String SECRET = "SHIRO+JWT";
public static String createToken(String username) {
try {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(SECRET);
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date)
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
return null;
}
}
public static boolean verify(String token, String username) {
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
}
3.测试
1.请求验证码 获取到 token:“24ce6a25-0415-4ce5-82fc-f71a734fe775” 2.请求登录接口 username 用户名 password 密码 code 验证码 token 验证码接口给到jwt,用来验证验证码的正确 3.登录成功后,response的请求头会多一个token 之后的请求,在请求头上加上token就会进行权限验证
|