First create a project, the project needs to have the function of logging in.
1. First need to use the token, store a token in the user table, generate a token with UUID every time the user logs in to replace the previous token, and save (modify) it in the cache.
2. Create @CurrentUser annotation.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER) // 可用在方法的参数上
@Retention(RetentionPolicy.RUNTIME) // 运行时有效
public @interface CurrentUser {
}
3. Write a component to store and take out the token
@Component
public class UserCache {
private static final String HELPER_KEY="user:temp:";
private static UserCache userCache;
@Resource
private TestUserService testUserService;
@Resource
private RedisTemplate<String, TestUser> redisTemplate;
@PostConstruct
public void init() {
userCache = this;
userCache.testUserService = this.testUserService;
userCache.redisTemplate=this.redisTemplate;
}
/**
* 服务器端的本地缓存时间为24小时
*/
private static final long EXPIRE_TIME = 24L;
/**
* 根据token获取Pregnant对象
*
* @param token
* @return
*/
public static TestUser getUserByToken(String token) {
TestUser testUser = userCache.redisTemplate.boundValueOps(HELPER_KEY + token).get();
if (testUser == null){
TestUser helper = userCache.testUserService.
findByToken(token);
if (helper == null){
throw new UnauthorizedException(ErrorCode.Unauthorized);
}
userCache.redisTemplate.boundValueOps(HELPER_KEY+token).set(testUser,EXPIRE_TIME, TimeUnit.HOURS);
}
return testUser;
}
/**
* 主动放入缓存
*
* @param token
* @param dto
*/
public static void put(String token, TestUser dto) {
userCache.redisTemplate.boundValueOps(HELPER_KEY+token).set(dto,EXPIRE_TIME,TimeUnit.HOURS);
}
4. Configure PublicInterface (used to mark that token is not required)
@Target(ElementType.METHOD) // 可用在方法的参数上
@Retention(RetentionPolicy.RUNTIME) // 运行时有效
public @interface PublicInterface {
}
5. Configure an interceptor to determine whether there is a token and whether the token is valid
public class AuthenticationInterceptor implements HandlerInterceptor {
/**
* 存储URL
*/
protected final static Set<String> ignoreUrls = ConcurrentHashMap.newKeySet();
private Logger logger = LoggerFactory.getLogger(AuthenticationInterceptor.class);
//不拦截哪些请求
static {
ignoreUrls.add("/error");
ignoreUrls.add("/images");
ignoreUrls.add("/static");
ignoreUrls.add("/webjars");
ignoreUrls.add("/Users/login");
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 判断接口是否需要登录
PublicInterface methodAnnotation = method.getAnnotation(PublicInterface.class);
if (methodAnnotation != null) {
return true;
}
if (isIntercept(request.getServletPath())) {
// 从http请求头中取出token
String token = request.getHeader("token");
if (token == null) {
throw new UnauthorizedException(ErrorCode.TokenNotExist);
}
//判断token时候合法
TestUser testUser = UserCache.getUserByToken(token);
if (testUser == null) {
throw new UnauthorizedException(ErrorCode.TokenInvalid);
}
// 将当前用户放入request以备@CurrentUser注解使用
request.setAttribute("currentUser", testUser);
return true;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
/**
* 判断是否需要拦截
*
* @param servletPath
* @return
*/
private boolean isIntercept(String servletPath) {
for (String url : ignoreUrls) {
if (servletPath.startsWith(url)) {
return false;
}
}
return true;
}
6. Configure the resolver to obtain the information of the currently logged-in user
public class UserMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().isAssignableFrom(TestUser.class)
&& parameter.hasParameterAnnotation(CurrentUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
TestUser dto = (TestUser) webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST);
if (dto != null) {
return dto;
}
throw new MissingServletRequestPartException("currentUser");
}
}
7. Inject into the program
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
//配置视图组件
@Bean
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/login.html").setViewName("login");
registry.addViewController("/home.html").setViewName("home");
registry.addViewController("/employee.html").setViewName("employee");
registry.addViewController("/addEmployee.html").setViewName("addEmployee");
registry.addViewController("/updateEmployee.html").setViewName("updateEmployee");
}
};
return adapter;
}
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(currentUserMethodArgumentResolver());
super.addArgumentResolvers(argumentResolvers);
}
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**");
super.addInterceptors(registry);
}
@Bean
public UserMethodArgumentResolver currentUserMethodArgumentResolver() {
return new UserMethodArgumentResolver();
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
8. The logged-in controller
@Controller
public class LoginController {
@Resource
TestUserService testUserService;
@PostMapping ("/Users/login")
@ResponseBody
public Map<String,String> userLogin(@RequestParam("userName") String userName,
@RequestParam("userPass") String userPass, HttpSession session){
Map<String,String> map = new HashMap<>();
TestUser user = testUserService.findUserLogin(userName, userPass);
if (user != null){
String token = UUID.randomUUID().toString().replaceAll("-", "");
//保存token到redis并更新数据库
user.setToken(token);
TestUser testUser = testUserService.updateTestUser(user);
session.setAttribute("token",token);
UserCache.put(token, testUser);
map.put("result","登录成功");
}else {
map.put("result","用户名或密码错误");
}
return map;
}
}
9. Login page
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
<script src="/js/jquery-2.1.3.min.js"></script>
</head>
<body>
<div>
账户:<input type="text" id="userName"/>
<br/>
密码:<input type="text" id="userPass"/>
<br/>
<input type="button" id="login" value="登录"/>
</div>
</body>
<script>
$("#login").click(function (){
let userName = $("#userName").val();
let userPass = $("#userPass").val();
$.ajax({
type:"POST",
url:"/Users/login",
data:{
userName:userName,
userPass:userPass
},
success:function (data){
if(data.result == "登录成功"){
alert("登录成功");
window.location.href = 'http://localhost:8080/home.html';
}else{
alert(data.result);
}
}
})
})
</script>
</html>
10. Jump to the page after logging in
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
<script src="/js/jquery-2.1.3.min.js"></script>
</head>
<body>
<input type="hidden" id="token" th:value="${session.token}"/>
<div>
<div>运营页面</div>
<div id="employee">员工管理</div>
<div>用户管理</div>
<div id="findEmployee">财务统计</div>
</div>
</body>
<script>
$("#employee").click(function (){
let token = $("#token").val();
$.ajax({
headers:{
token:token
},
type:"Get",
url:"/Users/findEmployeeAll",
date:{
},
success:function (data){
alert(data)
}
})
})
$("#findEmployee").click(function (){
let token = $("#token").val();
$.ajax({
type:"Get",
url:"/Users/findEmployee",
date:{
},
success:function (data){
alert(data)
}
})
})
</script>
</html>
11. Verification
/**
* 需要携带token的接口
* @return
*/
@GetMapping("/Users/findEmployeeAll")
@ResponseBody
public String findEmployeeAll(@CurrentUser TestUser user){
return "这是验证token的接口"+user.getToken();
}
/**
* 使用@PublicInterface,不需要携带token
* @param user
* @return
*/
@GetMapping("/Users/findEmployee")
@ResponseBody
@PublicInterface
public String findEmployee(TestUser user){
return "这是验证token的接口";
}