From 1ef676f9aef3e3ee30562dca599f921d862e842c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=99=AF=E9=98=B3?= Date: Sat, 30 Oct 2021 16:35:40 +0800 Subject: [PATCH] init --- .gitignore | 3 + pom.xml | 77 +++++++++++++++ .../anyin/gitee/shiro/ShiroApplication.java | 12 +++ .../gitee/shiro/base/BusinessCodeEnum.java | 24 +++++ .../gitee/shiro/base/BusinessException.java | 21 ++++ .../org/anyin/gitee/shiro/base/Response.java | 30 ++++++ .../anyin/gitee/shiro/base/SpringContext.java | 22 +++++ .../anyin/gitee/shiro/config/ShiroConfig.java | 64 +++++++++++++ .../shiro/controller/HomeController.java | 16 ++++ .../shiro/controller/LoginController.java | 30 ++++++ .../shiro/controller/form/LoginForm.java | 11 +++ .../org/anyin/gitee/shiro/model/UserInfo.java | 15 +++ .../gitee/shiro/service/ILoginService.java | 8 ++ .../gitee/shiro/service/ITokenService.java | 15 +++ .../gitee/shiro/service/IUserInfoService.java | 8 ++ .../shiro/service/impl/LoginServiceImpl.java | 41 ++++++++ .../shiro/service/impl/TokenServiceImpl.java | 46 +++++++++ .../service/impl/UserInfoServiceImpl.java | 28 ++++++ .../anyin/gitee/shiro/shiro/ShiroToken.java | 22 +++++ .../shiro/shiro/TokenCredentialsMatcher.java | 12 +++ .../anyin/gitee/shiro/shiro/TokenFilter.java | 95 +++++++++++++++++++ .../anyin/gitee/shiro/shiro/TokenRealm.java | 46 +++++++++ src/main/resources/application.yml | 3 + 23 files changed, 649 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/org/anyin/gitee/shiro/ShiroApplication.java create mode 100644 src/main/java/org/anyin/gitee/shiro/base/BusinessCodeEnum.java create mode 100644 src/main/java/org/anyin/gitee/shiro/base/BusinessException.java create mode 100644 src/main/java/org/anyin/gitee/shiro/base/Response.java create mode 100644 src/main/java/org/anyin/gitee/shiro/base/SpringContext.java create mode 100644 src/main/java/org/anyin/gitee/shiro/config/ShiroConfig.java create mode 100644 src/main/java/org/anyin/gitee/shiro/controller/HomeController.java create mode 100644 src/main/java/org/anyin/gitee/shiro/controller/LoginController.java create mode 100644 src/main/java/org/anyin/gitee/shiro/controller/form/LoginForm.java create mode 100644 src/main/java/org/anyin/gitee/shiro/model/UserInfo.java create mode 100644 src/main/java/org/anyin/gitee/shiro/service/ILoginService.java create mode 100644 src/main/java/org/anyin/gitee/shiro/service/ITokenService.java create mode 100644 src/main/java/org/anyin/gitee/shiro/service/IUserInfoService.java create mode 100644 src/main/java/org/anyin/gitee/shiro/service/impl/LoginServiceImpl.java create mode 100644 src/main/java/org/anyin/gitee/shiro/service/impl/TokenServiceImpl.java create mode 100644 src/main/java/org/anyin/gitee/shiro/service/impl/UserInfoServiceImpl.java create mode 100644 src/main/java/org/anyin/gitee/shiro/shiro/ShiroToken.java create mode 100644 src/main/java/org/anyin/gitee/shiro/shiro/TokenCredentialsMatcher.java create mode 100644 src/main/java/org/anyin/gitee/shiro/shiro/TokenFilter.java create mode 100644 src/main/java/org/anyin/gitee/shiro/shiro/TokenRealm.java create mode 100644 src/main/resources/application.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2e74c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.iml +.idea +target \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5bb6512 --- /dev/null +++ b/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.4.6 + + + + org.anyin.gitee.shiro + shiro-to-token + 1.0-SNAPSHOT + + + 2.4.6 + 1.8.0 + 5.7.2 + + + + + cn.hutool + hutool-all + ${hutool.version} + + + org.apache.shiro + shiro-spring-boot-web-starter + ${spring.boot.shiro.version} + + + + org.projectlombok + lombok + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + shiro-to-token + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + + + + + \ No newline at end of file diff --git a/src/main/java/org/anyin/gitee/shiro/ShiroApplication.java b/src/main/java/org/anyin/gitee/shiro/ShiroApplication.java new file mode 100644 index 0000000..f17ede4 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/ShiroApplication.java @@ -0,0 +1,12 @@ +package org.anyin.gitee.shiro; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ShiroApplication { + + public static void main(String[] args){ + SpringApplication.run(ShiroApplication.class, args); + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/base/BusinessCodeEnum.java b/src/main/java/org/anyin/gitee/shiro/base/BusinessCodeEnum.java new file mode 100644 index 0000000..2a7cf06 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/base/BusinessCodeEnum.java @@ -0,0 +1,24 @@ +package org.anyin.gitee.shiro.base; + +import lombok.Getter; + +@Getter +public enum BusinessCodeEnum { + USER_UN_LOGIN("1008", "用户未登录"), + USER_NOT_FOUND("B1001", "用户账号密码错误"), + PWD_NOT_MATCH("B1002", "用户账号密码错误"), + TOKEN_INVALID("B1003", "Token失效") + ; + + private final String code; + private final String msg; + + BusinessCodeEnum(String code, String msg) { + this.code = code; + this.msg = msg; + } + + public BusinessException getException(){ + return new BusinessException(this.code, this.msg); + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/base/BusinessException.java b/src/main/java/org/anyin/gitee/shiro/base/BusinessException.java new file mode 100644 index 0000000..b92e5d7 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/base/BusinessException.java @@ -0,0 +1,21 @@ +package org.anyin.gitee.shiro.base; + +import lombok.Data; + +@Data +public class BusinessException extends RuntimeException{ + + private String code; + + private String msg; + + public BusinessException(BusinessCodeEnum business){ + this.code = business.getCode(); + this.msg = business.getMsg(); + } + + public BusinessException(String code, String msg){ + this.code = code; + this.msg = code; + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/base/Response.java b/src/main/java/org/anyin/gitee/shiro/base/Response.java new file mode 100644 index 0000000..9ce86ff --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/base/Response.java @@ -0,0 +1,30 @@ +package org.anyin.gitee.shiro.base; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +public class Response { + + private static final String SUCCESS_CODE = "200"; + + private String code; + + private String msg; + + private T data; + + public Response(){ + this.code = SUCCESS_CODE; + } + + public Response(T data){ + this.data = data; + this.code = SUCCESS_CODE; + } + + public Response(String code, String msg){ + this.code = code; + this.msg = msg; + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/base/SpringContext.java b/src/main/java/org/anyin/gitee/shiro/base/SpringContext.java new file mode 100644 index 0000000..ccea72c --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/base/SpringContext.java @@ -0,0 +1,22 @@ +package org.anyin.gitee.shiro.base; + +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +@Component +public class SpringContext implements ApplicationContextAware { + + private static ApplicationContext context = null; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringContext.context = applicationContext; + } + + public static T getBean(Class type) { + return context.getBean(type); + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/config/ShiroConfig.java b/src/main/java/org/anyin/gitee/shiro/config/ShiroConfig.java new file mode 100644 index 0000000..f06c218 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/config/ShiroConfig.java @@ -0,0 +1,64 @@ +package org.anyin.gitee.shiro.config; + +import org.anyin.gitee.shiro.shiro.TokenCredentialsMatcher; +import org.anyin.gitee.shiro.shiro.TokenFilter; +import org.anyin.gitee.shiro.shiro.TokenRealm; +import org.apache.shiro.authc.credential.CredentialsMatcher; +import org.apache.shiro.authc.credential.HashedCredentialsMatcher; +import org.apache.shiro.crypto.hash.Sha256Hash; +import org.apache.shiro.mgt.SessionsSecurityManager; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition; +import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition; +import org.apache.shiro.web.filter.mgt.DefaultFilter; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.servlet.Filter; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +@Configuration +public class ShiroConfig { + + @Bean + public TokenRealm tokenRealm(){ + TokenRealm realm = new TokenRealm(); + realm.setCredentialsMatcher(credentialsMatcher()); + return realm; + } + + @Bean(name = "shiroFilterFactoryBean") + public ShiroFilterFactoryBean shiroFilterFactoryBean(){ + // 认证过滤器 + Map filterMap = new HashMap<>(); + filterMap.put("token", new TokenFilter()); + + // 忽略路径 + Map filterChainDefinitionMap = new LinkedHashMap<>(); + filterChainDefinitionMap.put("/login", DefaultFilter.anon.name()); + filterChainDefinitionMap.put("/logout", DefaultFilter.anon.name()); + filterChainDefinitionMap.put("/**", "token"); + + // 装配bean + ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean(); + filterFactoryBean.setSecurityManager(securityManager()); + filterFactoryBean.setFilters(filterMap); + filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); + return filterFactoryBean; + } + + @Bean + public CredentialsMatcher credentialsMatcher(){ + return new TokenCredentialsMatcher(); + } + + @Bean + public SessionsSecurityManager securityManager() { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); + securityManager.setRealm(tokenRealm()); + return securityManager; + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/controller/HomeController.java b/src/main/java/org/anyin/gitee/shiro/controller/HomeController.java new file mode 100644 index 0000000..70a1ed3 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/controller/HomeController.java @@ -0,0 +1,16 @@ +package org.anyin.gitee.shiro.controller; + +import org.anyin.gitee.shiro.base.Response; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/home") +public class HomeController { + + @GetMapping + public Response home(){ + return new Response<>("success"); + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/controller/LoginController.java b/src/main/java/org/anyin/gitee/shiro/controller/LoginController.java new file mode 100644 index 0000000..b4eb792 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/controller/LoginController.java @@ -0,0 +1,30 @@ +package org.anyin.gitee.shiro.controller; + +import org.anyin.gitee.shiro.base.Response; +import org.anyin.gitee.shiro.controller.form.LoginForm; +import org.anyin.gitee.shiro.model.UserInfo; +import org.anyin.gitee.shiro.service.ILoginService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping +public class LoginController { + + @Autowired + private ILoginService loginService; + + @PostMapping("/login") + public Response login(@RequestBody LoginForm form){ + String token = loginService.login(form.getUsername(), form.getPassword()); + return new Response<>(token); + } + + @PostMapping("/logout") + public Response logout(){ + return new Response<>(); + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/controller/form/LoginForm.java b/src/main/java/org/anyin/gitee/shiro/controller/form/LoginForm.java new file mode 100644 index 0000000..011a006 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/controller/form/LoginForm.java @@ -0,0 +1,11 @@ +package org.anyin.gitee.shiro.controller.form; + +import lombok.Data; + +@Data +public class LoginForm { + + private String username; + + private String password; +} diff --git a/src/main/java/org/anyin/gitee/shiro/model/UserInfo.java b/src/main/java/org/anyin/gitee/shiro/model/UserInfo.java new file mode 100644 index 0000000..df76513 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/model/UserInfo.java @@ -0,0 +1,15 @@ +package org.anyin.gitee.shiro.model; + +import lombok.Data; + +@Data +public class UserInfo { + + private Integer id; + + private String username; + + private String nickName; + + private String password; +} diff --git a/src/main/java/org/anyin/gitee/shiro/service/ILoginService.java b/src/main/java/org/anyin/gitee/shiro/service/ILoginService.java new file mode 100644 index 0000000..96debb1 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/service/ILoginService.java @@ -0,0 +1,8 @@ +package org.anyin.gitee.shiro.service; + +public interface ILoginService { + + String login(String username, String password); + + void logout(String token); +} diff --git a/src/main/java/org/anyin/gitee/shiro/service/ITokenService.java b/src/main/java/org/anyin/gitee/shiro/service/ITokenService.java new file mode 100644 index 0000000..5c30193 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/service/ITokenService.java @@ -0,0 +1,15 @@ +package org.anyin.gitee.shiro.service; + +import org.anyin.gitee.shiro.model.UserInfo; +import org.anyin.gitee.shiro.shiro.ShiroToken; + +public interface ITokenService { + + boolean check(String token); + + ShiroToken createToken(String token, UserInfo userInfo); + + ShiroToken createToken(UserInfo userInfo); + + UserInfo getUserInfo(String token); +} diff --git a/src/main/java/org/anyin/gitee/shiro/service/IUserInfoService.java b/src/main/java/org/anyin/gitee/shiro/service/IUserInfoService.java new file mode 100644 index 0000000..aa3f80b --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/service/IUserInfoService.java @@ -0,0 +1,8 @@ +package org.anyin.gitee.shiro.service; + +import org.anyin.gitee.shiro.model.UserInfo; + +public interface IUserInfoService { + + UserInfo findByUsername(String username); +} diff --git a/src/main/java/org/anyin/gitee/shiro/service/impl/LoginServiceImpl.java b/src/main/java/org/anyin/gitee/shiro/service/impl/LoginServiceImpl.java new file mode 100644 index 0000000..487559b --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/service/impl/LoginServiceImpl.java @@ -0,0 +1,41 @@ +package org.anyin.gitee.shiro.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.anyin.gitee.shiro.base.BusinessCodeEnum; +import org.anyin.gitee.shiro.model.UserInfo; +import org.anyin.gitee.shiro.service.ILoginService; +import org.anyin.gitee.shiro.service.ITokenService; +import org.anyin.gitee.shiro.service.IUserInfoService; +import org.anyin.gitee.shiro.shiro.ShiroToken; +import org.apache.shiro.SecurityUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class LoginServiceImpl implements ILoginService { + + @Autowired + private IUserInfoService userInfoService; + + @Autowired + private ITokenService tokenService; + + @Override + public String login(String username, String password) { + UserInfo userInfo = userInfoService.findByUsername(username); + if(!userInfo.getPassword().equals(password)){ + throw BusinessCodeEnum.PWD_NOT_MATCH.getException(); + } + // shiro 框架登录 + ShiroToken token = tokenService.createToken(userInfo); + SecurityUtils.getSubject().login(token); + + return (String) token.getCredentials(); + } + + @Override + public void logout(String token) { + + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/service/impl/TokenServiceImpl.java b/src/main/java/org/anyin/gitee/shiro/service/impl/TokenServiceImpl.java new file mode 100644 index 0000000..434adf8 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/service/impl/TokenServiceImpl.java @@ -0,0 +1,46 @@ +package org.anyin.gitee.shiro.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.anyin.gitee.shiro.model.UserInfo; +import org.anyin.gitee.shiro.service.ITokenService; +import org.anyin.gitee.shiro.shiro.ShiroToken; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Service +@Slf4j +public class TokenServiceImpl implements ITokenService { + + private Map tokenUserMap = new HashMap<>(); + + @Override + public boolean check(String token) { + log.info("check token is valid"); + return true; + } + + @Override + public ShiroToken createToken(String token, UserInfo userInfo) { + String targetToken = token; + if(ObjectUtils.isEmpty(targetToken)){ + targetToken = UUID.randomUUID().toString(); + } + // token 和 用户信息的映射,可以使用Redis存储 或者 替换为JWT + tokenUserMap.put(targetToken, userInfo); + return new ShiroToken(targetToken); + } + + @Override + public ShiroToken createToken(UserInfo userInfo) { + return this.createToken("", userInfo); + } + + @Override + public UserInfo getUserInfo(String token) { + return tokenUserMap.get(token); + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/service/impl/UserInfoServiceImpl.java b/src/main/java/org/anyin/gitee/shiro/service/impl/UserInfoServiceImpl.java new file mode 100644 index 0000000..2d3a042 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/service/impl/UserInfoServiceImpl.java @@ -0,0 +1,28 @@ +package org.anyin.gitee.shiro.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.anyin.gitee.shiro.base.BusinessCodeEnum; +import org.anyin.gitee.shiro.model.UserInfo; +import org.anyin.gitee.shiro.service.IUserInfoService; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class UserInfoServiceImpl implements IUserInfoService { + + private static final String DEFAULT_USERNAME = "anyin"; + + @Override + public UserInfo findByUsername(String username) { + // 从数据库查询用户信息 + if(DEFAULT_USERNAME.equals(username)){ + UserInfo userInfo = new UserInfo(); + userInfo.setId(1); + userInfo.setNickName("暗音"); + userInfo.setUsername("anyin"); + userInfo.setPassword("123456"); + return userInfo; + } + throw BusinessCodeEnum.USER_NOT_FOUND.getException(); + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/shiro/ShiroToken.java b/src/main/java/org/anyin/gitee/shiro/shiro/ShiroToken.java new file mode 100644 index 0000000..2628f60 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/shiro/ShiroToken.java @@ -0,0 +1,22 @@ +package org.anyin.gitee.shiro.shiro; + +import org.apache.shiro.authc.AuthenticationToken; + +public class ShiroToken implements AuthenticationToken { + + private String token; + + public ShiroToken(String token) { + this.token = token; + } + + @Override + public Object getPrincipal() { + return token; + } + + @Override + public Object getCredentials() { + return token; + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/shiro/TokenCredentialsMatcher.java b/src/main/java/org/anyin/gitee/shiro/shiro/TokenCredentialsMatcher.java new file mode 100644 index 0000000..c586146 --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/shiro/TokenCredentialsMatcher.java @@ -0,0 +1,12 @@ +package org.anyin.gitee.shiro.shiro; + +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.credential.CredentialsMatcher; + +public class TokenCredentialsMatcher implements CredentialsMatcher { + @Override + public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { + return token instanceof ShiroToken; + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/shiro/TokenFilter.java b/src/main/java/org/anyin/gitee/shiro/shiro/TokenFilter.java new file mode 100644 index 0000000..91ec11d --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/shiro/TokenFilter.java @@ -0,0 +1,95 @@ +package org.anyin.gitee.shiro.shiro; + +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.anyin.gitee.shiro.base.BusinessCodeEnum; +import org.anyin.gitee.shiro.base.Response; +import org.anyin.gitee.shiro.base.SpringContext; +import org.anyin.gitee.shiro.service.ITokenService; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.web.filter.authc.AuthenticatingFilter; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.RequestMethod; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Slf4j +public class TokenFilter extends AuthenticatingFilter { + + private static final String X_TOKEN = "X-Token"; + + private ITokenService tokenService = null; + + /** + * 创建Token, 支持自定义Token + */ + @Override + protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { + String token = this.getToken((HttpServletRequest)servletRequest); + if(ObjectUtils.isEmpty(token)){ + log.error("token is empty"); + return null; + } + return new ShiroToken(token); + } + + /** + * 兼容跨域 + */ + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + return ((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name()); + } + + @Override + protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { + HttpServletRequest request = (HttpServletRequest)servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + + String token = this.getToken(request); + if(ObjectUtils.isEmpty(token)){ + this.respUnLogin(request, response); + return false; + } + + // 校验Token的有效性 + if(tokenService == null){ + tokenService = SpringContext.getBean(ITokenService.class); + } + + if(!tokenService.check(token)){ + this.respUnLogin(request, response); + } + + // 根据token获取用户信息,会执行 TokenRealm#doGetAuthenticationInfo 方法 + return executeLogin(servletRequest, servletResponse); + } + + private void respUnLogin(HttpServletRequest request, HttpServletResponse response) throws IOException { + response.setContentType("application/json;charset=utf-8"); + response.setHeader("Access-Control-Allow-Credentials", "true"); + response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); + + Response resp = new Response(BusinessCodeEnum.USER_UN_LOGIN.getCode(), BusinessCodeEnum.USER_UN_LOGIN.getMsg()); + response.getWriter().print(JSONUtil.toJsonStr(resp)); + } + + /** + * 获取token + * 优先从header获取 + * 如果没有,则从parameter获取 + * @param request request + * @return token + */ + private String getToken(HttpServletRequest request){ + String token = request.getHeader(X_TOKEN); + if(ObjectUtils.isEmpty(token)){ + token = request.getParameter(X_TOKEN); + } + return token; + } +} diff --git a/src/main/java/org/anyin/gitee/shiro/shiro/TokenRealm.java b/src/main/java/org/anyin/gitee/shiro/shiro/TokenRealm.java new file mode 100644 index 0000000..013c6fc --- /dev/null +++ b/src/main/java/org/anyin/gitee/shiro/shiro/TokenRealm.java @@ -0,0 +1,46 @@ +package org.anyin.gitee.shiro.shiro; + +import lombok.extern.slf4j.Slf4j; +import org.anyin.gitee.shiro.base.BusinessCodeEnum; +import org.anyin.gitee.shiro.model.UserInfo; +import org.anyin.gitee.shiro.service.ITokenService; +import org.anyin.gitee.shiro.service.IUserInfoService; +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.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; + +@Slf4j +public class TokenRealm extends AuthorizingRealm { + + @Autowired + @Lazy + private ITokenService tokenService; + + @Override + public boolean supports(AuthenticationToken token) { + return token instanceof ShiroToken; + } + + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { + log.info("user do authorization: {}", principalCollection); + return null; + } + + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { + log.info("user do authentication: {}", authenticationToken); + ShiroToken token = (ShiroToken)authenticationToken; + UserInfo userInfo = tokenService.getUserInfo(token.getCredentials().toString()); + if(userInfo == null){ + throw BusinessCodeEnum.TOKEN_INVALID.getException(); + } + return new SimpleAuthenticationInfo(userInfo.getUsername(), userInfo.getPassword(), userInfo.getNickName()); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..458884f --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + application: + name: shiro-to-token \ No newline at end of file