现在大部分应用前后端分离鲜明的,前端不要接触过多的业务逻辑,都由后端解决,基本思路是这样的:服务端通过 JSON字符串,告诉前端用户有没有登录、认证,前端根据这些提示跳转对应的登录页、认证页等。
在学习springboot过程中,研究了下spring security使用JWT代替Session的方案,并适配了前后端分离时的ajax请求的Json返回。
此代码有很多不足,用到生产环境,需要做一些改动,如springsecurity中授权的配置是硬代码,用户权限修改后的刷新,Token在分布式环境下的存储等。
1、向Mysql导入基本使用的表结构
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT '',
`descritpion` varchar(10) DEFAULT '',
`url` varchar(10) DEFAULT '',
`pid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
/*Data for the table `sys_permission` */
insert into `sys_permission`(`id`,`name`,`descritpion`,`url`,`pid`) values (3,'ADMIN','','',NULL),(4,'USER','','',NULL);
/*Table structure for table `sys_permission_role` */
DROP TABLE IF EXISTS `sys_permission_role`;
CREATE TABLE `sys_permission_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) DEFAULT NULL,
`permission_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
/*Data for the table `sys_permission_role` */
insert into `sys_permission_role`(`id`,`role_id`,`permission_id`) values (1,2,4),(2,1,3);
/*Table structure for table `sys_role` */
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
/*Data for the table `sys_role` */
insert into `sys_role`(`id`,`name`) values (1,'ADMIN'),(2,'USER');
/*Table structure for table `sys_role_user` */
DROP TABLE IF EXISTS `sys_role_user`;
CREATE TABLE `sys_role_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Sys_user_id` int(11) DEFAULT NULL,
`sys_role_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
/*Data for the table `sys_role_user` */
insert into `sys_role_user`(`id`,`Sys_user_id`,`sys_role_id`) values (1,2,1),(2,3,2);
/*Table structure for table `sys_user` */
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
/*Data for the table `sys_user` */
insert into `sys_user`(`id`,`name`,`password`) values (2,'admin','$2a$10$Yks2LoqzBUHEWjyLCnsdtepI4oCNip9yNdf67y19ewF8geORNAO5m');
2、引入依赖
这里使用的gradle构建,使用maven时将依赖替换为maven的就好
使用springboot的版本为2.4.3,经测试默认的tomcat使用过程中控制台会一直报错,使用undertow做了替换。springboot的插件配置略。
configurations {
compile.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
compile.exclude group: 'org.apache.tomcat'
compile.exclude group: 'org.apache.tomcat.embed'
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-security"
compile group: 'mysql', name: 'mysql-connector-java', version: "8.0.15"
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-undertow'
compile 'javax:javaee-api:7.0'
compile group: 'com.auth0', name: 'java-jwt', version: "3.3.0"
}
3、几个实体和Dao
这里用了JPA访问数据库
使用了lombok插件,自动生成get/set,依赖里没有引入是因为在我的父模块中已经引入过了
package com.iscas.biz.security.domain;
import javax.persistence.*;
@Entity
@Table(name = "sys_permission")
@Data
public class Permission {
@Id
@Column(name="id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
//权限名称
@Column(name="name")
private String name;
//权限描述
@Column(name = "descritpion")
private String descritpion;
//授权链接
@Column(name = "url")
private String url;
//父节点id
@Column(name = "pid")
private int pid;
}
package com.iscas.biz.security.domain;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PermissionRepository extends CrudRepository<Permission,Integer> {
}
package com.iscas.biz.security.domain;
import javax.persistence.*;
@Entity
@Table(name="sys_role")
@Data
public class SysRole {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name")
private String name;
}
package com.iscas.biz.security.domain;
import org.springframework.data.repository.CrudRepository;
public interface SysRoleRepository extends CrudRepository<SysRole,Integer> {
}
package com.iscas.biz.security.domain;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "sys_user")
@Data
public class SysUser {
@Id
@Column(name="id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "password")
private String password;
@Transient
private List<SysRole> roles;
@Override
public String toString() {
return "SysUser{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", roles=" + roles +
'}';
}
}
package com.iscas.biz.security.domain;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface SysUserRepository extends CrudRepository<SysUser,Integer> {
@Query("select a from SysUser a where a.name=:name")
public SysUser getUserByName(@Param("name") String name);
}
4、几个访问数据库的service
package com.iscas.biz.security.service;
import com.iscas.biz.security.domain.Permission;
import java.util.List;
public interface PermissionService {
public List<Permission> findAll();
public List<Permission> findByAdminUserId(int userId);
}
package com.iscas.biz.security.service;
import com.iscas.biz.security.domain.Permission;
import com.iscas.biz.security.domain.PermissionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/2/27 14:54
* @since jdk1.8
*/
@Service
public class PermissionServiceImpl implements PermissionService {
@Autowired
PermissionRepository permissionRepository;
@PersistenceContext
private EntityManager entityManager;
@Override
public List<Permission> findAll() {
return null;
}
@Override
public List<Permission> findByAdminUserId(int userId) {
List<Permission> list = new ArrayList<Permission>();
List<Object[]> abcs = entityManager.createNativeQuery("select p.* \n" +
" from Sys_User u\n" +
" LEFT JOIN sys_role_user sru on u.id= sru.Sys_User_id\n" +
" LEFT JOIN Sys_Role r on sru.Sys_Role_id=r.id\n" +
" LEFT JOIN Sys_permission_role spr on spr.role_id=r.id\n" +
" LEFT JOIN Sys_permission p on p.id =spr.permission_id\n" +
" where u.id=" + userId).getResultList();
for (Object[] abc : abcs) {
Permission permission = new Permission();
permission.setId(Integer.valueOf(abc[0] + ""));
permission.setName(abc[1] + "");
permission.setDescritpion(abc[2] + "");
permission.setUrl(abc[3] + "");
// permission.setPid(Integer.valueOf(abc[4]+""));
list.add(permission);
}
return list;
}
}
5、自定义springsecurtiy的UserdetailSerivice
package com.iscas.biz.security.service;
import com.iscas.biz.security.domain.Permission;
import com.iscas.biz.security.domain.SysUser;
import com.iscas.biz.security.domain.SysUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
//自定义userdetailservice
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
SysUserRepository sysUserRepository;
@Autowired
PermissionService permissionService;
// @Autowired
// PasswordEncoder passwordEncoder;
@Transactional
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserRepository.getUserByName(username);
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
if (sysUser != null) {
System.err.println("sysUser===============" + sysUser);
//获取用户的授权
List<Permission> permissions = permissionService.findByAdminUserId(sysUser.getId());
//声明授权文件
for (Permission permission : permissions) {
if (permission != null && permission.getName() != null) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+permission.getName());
grantedAuthorities.add(grantedAuthority);
}
}
}
System.err.println("grantedAuthorities===============" + grantedAuthorities);
return new User(sysUser.getName(), sysUser.getPassword(), grantedAuthorities);
}
}
6、自定义授权失败处理,返回JSON,而不是页面
package com.iscas.biz.security.handler;
import com.iscas.biz.security.util.OutputUtils;
import com.iscas.common.web.tools.json.JsonUtils;
import com.iscas.templet.common.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
* 授权失败处理
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/03/14 15:09
* @since jdk1.8
*/
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
ResponseEntity responseEntity = new ResponseEntity();
responseEntity.setMessage("没有权限");
OutputUtils.output(httpServletResponse, 403, responseEntity);
}
}
7、自定义未登录处理,返回JSON,而不是页面
package com.iscas.biz.security.handler;
import com.iscas.biz.security.util.OutputUtils;
import com.iscas.common.web.tools.json.JsonUtils;
import com.iscas.templet.common.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
* 未登陆
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/03/14 14:30
* @since jdk1.8
*/
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
ResponseEntity responseEntity = new ResponseEntity();
responseEntity.setMessage("未登录");
OutputUtils.output(httpServletResponse, 401, responseEntity);
}
}
8、自定义登录失败处理,返回JSON,而不是页面
package com.iscas.biz.security.handler;
import com.iscas.biz.security.util.OutputUtils;
import com.iscas.common.web.tools.json.JsonUtils;
import com.iscas.templet.common.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
* 登陆失败处理
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/03/14 14:32
* @since jdk1.8
*/
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
ResponseEntity responseEntity = new ResponseEntity();
responseEntity.setMessage("登录失败");
OutputUtils.output(httpServletResponse, 401, responseEntity);
}
}
9、自定义登录成功处理,返回JSON,而不是页面
package com.iscas.biz.security.handler;
import com.iscas.biz.security.util.JWTUtils;
import com.iscas.biz.security.util.OutputUtils;
import com.iscas.templet.common.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
* 登陆成功处理
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/03/14 14:33
* @since jdk1.8
*/
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
ResponseEntity responseEntity = new ResponseEntity();
responseEntity.setMessage("登录成功");
UserDetails user = (UserDetails) authentication.getPrincipal();
//生成JWT token
String jwtToken = JWTUtils.createToken(user.getUsername(), 5);
responseEntity.setValue(jwtToken);
OutputUtils.output(httpServletResponse, 200, responseEntity);
}
}
10、自定义登出处理,返回JSON,而不是页面
package com.iscas.biz.security.handler;
import com.iscas.biz.security.util.JWTUtils;
import com.iscas.biz.security.util.OutputUtils;
import com.iscas.common.web.tools.json.JsonUtils;
import com.iscas.templet.common.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
* 登出处理
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/03/14 15:16
* @since jdk1.8
*/
@Component
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
ResponseEntity responseEntity = new ResponseEntity();
String authorization = httpServletRequest.getHeader("Authorization");
JWTUtils.TOKENS.remove(authorization);
responseEntity.setMessage("登出成功");
OutputUtils.output(httpServletResponse, 200, responseEntity);
}
}
11、自定义token校验过滤器
package com.iscas.biz.security.filter;
import com.auth0.jwt.interfaces.Claim;
import com.iscas.biz.security.util.JWTUtils;
import com.iscas.biz.security.util.OutputUtils;
import com.iscas.templet.common.ResponseEntity;
import com.iscas.templet.exception.ValidTokenException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
@SuppressWarnings("ALL")
//@Component
public class JwtAuthenticationTokenFilter extends BasicAuthenticationFilter {
// @Autowired
private UserDetailsService userDetailsService;
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
//
private AuthenticationManager authenticationManager;
public JwtAuthenticationTokenFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
this.authenticationManager = authenticationManager;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader == null) {
//如果没有传递Authorization,传递给下一个过滤器处理
doFilter(request, response, chain);
return;
}
try {
if (!JWTUtils.TOKENS.contains(authHeader)) {
throw new ValidTokenException("token已被删除");
}
Map<String, Claim> stringClaimMap = JWTUtils.verifyToken(authHeader);
String username = stringClaimMap.get("username").asString();
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (userDetails != null) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
} catch (ValidTokenException e) {
// throw new RuntimeException(e);
ResponseEntity responseEntity = new ResponseEntity();
responseEntity.setMessage("权限校验出错");
responseEntity.setDesc(e.getMessage());
OutputUtils.output(response, 403, responseEntity);
return;
}
chain.doFilter(request, response);
}
}
12、配置security
package com.iscas.biz.security.config;
import com.iscas.biz.security.filter.JwtAuthenticationTokenFilter;
import com.iscas.biz.security.handler.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 参考 :
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/2/27 14:35
* @since jdk1.8
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
// 未登陆时返回 JSON 格式的数据给前端(否则为 html)
private final CustomAuthenticationEntryPoint authenticationEntryPoint;
// 登录成功返回的 JSON 格式数据给前端(否则为 html)
private final CustomAuthenticationSuccessHandler authenticationSuccessHandler;
// 登录失败返回的 JSON 格式数据给前端(否则为 html)
private final CustomAuthenticationFailureHandler authenticationFailureHandler;
// 注销成功返回的 JSON 格式数据给前端(否则为 登录时的 html)
private final CustomLogoutSuccessHandler logoutSuccessHandler;
// 无权访问返回的 JSON 格式数据给前端(否则为 403 html 页面)
private final CustomAccessDeniedHandler accessDeniedHandler;
// 自定义安全认证
// private final CustomAuthenticationProvider provider;
// JWT 拦截器
// private final JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public WebSecurityConfig(UserDetailsService userDetailsService, CustomAuthenticationEntryPoint authenticationEntryPoint,
CustomAuthenticationSuccessHandler authenticationSuccessHandler, CustomAuthenticationFailureHandler authenticationFailureHandler, CustomLogoutSuccessHandler logoutSuccessHandler, CustomAccessDeniedHandler accessDeniedHandler /*, CustomAuthenticationProvider provider,*/ /*JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter*/) {
this.userDetailsService = userDetailsService;
this.authenticationEntryPoint = authenticationEntryPoint;
this.authenticationSuccessHandler = authenticationSuccessHandler;
this.authenticationFailureHandler = authenticationFailureHandler;
this.logoutSuccessHandler = logoutSuccessHandler;
this.accessDeniedHandler = accessDeniedHandler;
// this.provider = provider;
// this.jwtAuthenticationTokenFilter = jwtAuthenticationTokenFilter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter = new JwtAuthenticationTokenFilter(authenticationManager());
jwtAuthenticationTokenFilter.setUserDetailsService(userDetailsService);
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 使用 JWT,关闭session
.and()
.httpBasic().authenticationEntryPoint(authenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/decision/**","/govern/**").hasAnyRole("USER","ADMIN")//对decision和govern 下的接口 需要 USER 或者 ADMIN 权限
.antMatchers("/admin/login").permitAll()///admin/login 不限定
.antMatchers("/admin/**").hasRole("ADMIN")//对admin下的接口 需要ADMIN权限
.antMatchers("/admin2/**").hasRole("AAA")//对admin2下的接口 需要AAA权限
.antMatchers("/oauth/**").permitAll()//不拦截 oauth 开放的资源
.antMatchers("/admin3/**").permitAll()//不拦截 admin3 开放的资源
// .anyRequest()
// .access("@rbacauthorityservice.hasPermission(request, authentication)")
.and()
.formLogin() //开启登录
.successHandler(authenticationSuccessHandler) // 登录成功
.failureHandler(authenticationFailureHandler) // 登录失败
.permitAll()
.and()
.logout()
.logoutSuccessHandler(logoutSuccessHandler)
.permitAll()
.and()
.authorizeRequests()
.anyRequest().permitAll()//其他没有限定的请求,允许访问
// .and().anonymous()//对于没有配置权限的其他请求允许匿名访问
// ;
.and()
.addFilter(jwtAuthenticationTokenFilter); // JWT Filter
// 记住我
http.rememberMe().rememberMeParameter("remember-me")
.userDetailsService(userDetailsService).tokenValiditySeconds(300);
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); // 无权访问 JSON 格式的数据
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.authenticationProvider(provider);
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
}
13、定义几个测试controller
package com.iscas.biz.security.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("admin")
public class MainController {
@RequestMapping("/")
public String index(){
return "index" ;
}
@RequestMapping("/detail")
public String hello(){
return "hello" ;
}
@RequestMapping("/login")
public String login() {
return "login";
}
}
package com.iscas.biz.security.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("admin2")
public class Main2Controller {
@RequestMapping("/")
public String index(){
return "index" ;
}
@RequestMapping("/detail")
public String hello(){
return "hello" ;
}
@RequestMapping("/login")
public String login() {
return "login";
}
}
package com.iscas.biz.security.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("admin3")
public class Main3Controller {
@RequestMapping("/")
public String index(){
return "index" ;
}
@RequestMapping("/detail")
public String hello(){
return "hello" ;
}
@RequestMapping("/login")
public String login() {
return "login";
}
}
14、用到的几个工具类
package com.iscas.biz.security.util;
import com.iscas.common.web.tools.json.JsonUtils;
import com.iscas.templet.common.ResponseEntity;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 输出工具类
*
* @author zhuquanwen
* @vesion 1.0
* @date 2018/7/17 17:33
* @since jdk1.8
*/
public class OutputUtils {
private OutputUtils(){}
public static void output(HttpServletResponse response, int status, String msg, String desc) throws IOException {
response.setContentType("application/json; charset=utf-8");
response.setCharacterEncoding("UTF-8");
response.setStatus(status);
ServletOutputStream pw = response.getOutputStream();
ResponseEntity responseEntity = new ResponseEntity(status,msg);
responseEntity.setDesc(desc);
pw.write(JsonUtils.toJson(responseEntity).getBytes("UTF-8"));
pw.flush();
// pw.close();
}
public static void output(HttpServletResponse response, int status, ResponseEntity responseEntity) throws IOException {
response.setContentType("application/json; charset=utf-8");
response.setCharacterEncoding("UTF-8");
response.setStatus(status);
ServletOutputStream pw = response.getOutputStream();
pw.write(JsonUtils.toJson(responseEntity).getBytes("UTF-8"));
pw.flush();
// pw.close();
}
}
package com.iscas.biz.security.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.iscas.templet.exception.ValidTokenException;
import java.io.UnsupportedEncodingException;
import java.util.*;
/**
* JWT工具类
*
* @author zhuquanwen
* @vesion 1.0
* @date 2018/7/16 22:29
* @since jdk1.8
*/
public class JWTUtils {
public static Set<String> TOKENS = new HashSet<>();
private JWTUtils(){}
public static final String SECRET = "ISCAS";
public static String createToken(String username, int expire) throws UnsupportedEncodingException {
Date iatDate = new Date();
// Calendar nowTime = Calendar.getInstance();
// nowTime.add(Calendar.MINUTE,expire);
// Date expiresDate = nowTime.getTime();
Date expiresDate = DateRaiseUtils.afterOffsetDate(expire * 60 * 1000L);
Map<String, Object> map = new HashMap<>(2 << 2);
map.put("alg", "HS256");
map.put("typ","JWT");
String token = JWT.create()
.withHeader(map)
.withClaim("username", username)
.withClaim("date", iatDate)
.withExpiresAt(expiresDate)
.withIssuedAt(iatDate)
.sign(Algorithm.HMAC256(SECRET));
TOKENS.add(token);
//将token缓存起来
// CaffCacheUtils.set(token, iatDate);
// IAuthCacheService authCacheService = SpringService.getApplicationContext().getBean(IAuthCacheService.class);
// authCacheService.set(token, iatDate);
return token;
}
public static Map<String, Claim> verifyToken(String token) throws UnsupportedEncodingException, ValidTokenException {
// Object obj = CaffCacheUtils.get(token);
// IAuthCacheService authCacheService = SpringService.getApplicationContext().getBean(IAuthCacheService.class);
// Object obj = authCacheService.get(token);
// if(obj == null){
// throw new ValidTokenException("登录凭证校验失败","token:" + token + "不存在或已经被注销");
// }
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
DecodedJWT decodedJWT = null;
try {
decodedJWT = jwtVerifier.verify(token);
}catch (Exception e){
throw new ValidTokenException("登录凭证校验失败","token:" + token + "校验失败");
}
return decodedJWT.getClaims();
}
}
package com.iscas.biz.security.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* <p>线程安全的时间转化工具类</p>
* @author zhuquanwen
* @version 1.0
* @since jdk1.8
* @date 2018/7/16
**/
public class DateSafeUtils {
private DateSafeUtils(){}
public static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
/**
* 锁对象
*/
private static final Object LOCK_OBJ = new Object();
/**
* 存放不同的日期模板格式的sdf的Map
*/
private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();
/**
* 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
*
* @param pattern
* @return
*/
private static SimpleDateFormat getSdf(final String pattern) {
ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);
// 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
if (tl == null) {
synchronized (LOCK_OBJ) {
tl = sdfMap.get(pattern);
if (tl == null) {
// 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
// System.out.println("put new sdf of pattern " + pattern + " to map");
// 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
tl = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern);
}
};
sdfMap.put(pattern, tl);
}
}
}
return tl.get();
}
/**
* 使用ThreadLocal<SimpleDateFormat>来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
* 如果新的线程中没有SimpleDateFormat,才会new一个
* @param date {@link Date} 时间
* @param pattern 格式化串
* @return String 时间字符串格式
*/
public static String format(Date date, String pattern) {
return getSdf(pattern).format(date);
}
/**
* 使用ThreadLocal<SimpleDateFormat>来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
* 如果新的线程中没有SimpleDateFormat,才会new一个
* @param dateStr 字符串
* @param pattern 时间字符串格式
* @throws ParseException 时间转换错误
* @return Date {@link Date} 时间
*/
public static Date parse(String dateStr, String pattern) throws ParseException {
return getSdf(pattern).parse(dateStr);
}
}
package com.iscas.biz.security.util;
import java.util.Date;
/**
* <p>date操作增强类</p>
* @author zhuquanwen
* @version 1.0
* @since jdk1.8
*/
public class DateRaiseUtils {
private DateRaiseUtils(){}
/**
* 获取当前日期里的年份
* @version 1.0
* @since jdk1.8
* @param date 日期
* @return int 年份
* @see DateSafeUtils
*/
public static int getYear(Date date){
assert date != null;
String x = DateSafeUtils.format(date, "yyyy");
return Integer.parseInt(x);
}
/**
* 获取当前日期里的月份
* @version 1.0
* @since jdk1.8
* @param date 日期
* @return int 月份
* @see DateSafeUtils
*/
public static int getMonth(Date date){
assert date != null;
String month = DateSafeUtils.format(date, "MM");
return Integer.parseInt(month);
}
/**
* 获取当前日期里的天
* @version 1.0
* @since jdk1.8
* @param date 日期
* @return int 天
* @see DateSafeUtils
*/
public static int getDay(Date date){
assert date != null;
String x = DateSafeUtils.format(date, "dd");
return Integer.parseInt(x);
}
/**
* 获取当前日期里的小时
* @version 1.0
* @since jdk1.8
* @param date 日期
* @return int 小时
* @see DateSafeUtils
*/
public static int getHour(Date date){
assert date != null;
String x = DateSafeUtils.format(date, "HH");
return Integer.parseInt(x);
}
/**
* 获取当前日期里的分钟
* @version 1.0
* @since jdk1.8
* @param date 日期
* @return int 分钟
* @see DateSafeUtils
*/
public static int getMinute(Date date){
assert date != null;
String x = DateSafeUtils.format(date, "HH");
return Integer.parseInt(x);
}
/**
* 获取当前日期里的秒数
* @version 1.0
* @since jdk1.8
* @param date 日期
* @return int 秒数
* @see DateSafeUtils
*/
public static int getSecond(Date date){
assert date != null;
String x = DateSafeUtils.format(date, "ss");
return Integer.parseInt(x);
}
// /**
// * 判断当前月份是否是季度末
// * @version 1.0
// * @since jdk1.8
// * @param date 时间
// * @return boolean
// * @see #getMonth(Date)
// */
@SuppressWarnings("AlibabaUndefineMagicConstant")
// public static boolean isSeason(Date date){
// assert date != null;
// boolean sign = false;
// int month = getMonth(date);
// if (month == MonthEnum.MAR.getValue()) {
// sign = true;
// }
// if (month == MonthEnum.JULY.getValue()) {
// sign = true;
// }
// if (month == MonthEnum.SEPT.getValue()) {
// sign = true;
// }
// if (month == MonthEnum.DEC.getValue()) {
// sign = true;
// }
// return sign;
// }
/**
* 计算从现在开始偏移毫秒数后的时间,支持负数
* @version 1.0
* @since jdk1.8
* @param offset 偏移的时候毫秒数
* @return java.util.Date
* @see #afterOffsetDate(Date, long)
*/
public static Date afterOffsetDate(long offset){
return afterOffsetDate(new Date(), offset);
}
/**
* 计算从某个时间偏移毫秒数后的时间,支持负数
* @version 1.0
* @since jdk1.8
* @param offset 偏移的时候毫秒数
* @param date 日期时间
* @return java.util.Date
*/
public static Date afterOffsetDate(Date date, long offset){
assert date != null;
long time = date.getTime() * 1L;
time = time + offset;
return new Date(time);
}
/**
* 时间偏移一定毫秒数
* @see {@link #afterOffsetDate(Date, long)}
* */
@Deprecated
public static Date timeOffset(Date time, long offset) {
time = new Date(time.getTime() + offset);
return time;
}
}
package com.iscas.common.web.tools.json;
import cn.miludeer.jsoncode.JsonCode;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections.CollectionUtils;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @program: stc-pub
* @description: JSON工具类
* @author: LiangJian
* @create: 2018-08-29 09:56
**/
public class JsonUtils {
private static ObjectMapper mapper;
/**
* 对象转json
*
* @param object
* @return
*/
public static String toJson(Object object){
try {
return getMapper().writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
// throw new DataSongException(Status.PARAM_ERROR, String.format("object to json error: [%s]",DataSongExceptionUtils.getExceptionInfo(e)));
}
// return null;
}
public static <T> T fromJson(String json, Class<T > classOfT) {
// return gson.fromJson(json, classOfT);
try {
return getMapper().readValue(json, classOfT);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
// return null;
}
/**
* @param json
* @param typeReference
* @param <T> new TypeReference<HashMap<String,Field>>() {}
* @return
*/
public static <T> T fromJson(String json, TypeReference typeReference) {
try {
return getMapper().readValue(json, typeReference);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
// return null;
}
/**
* 定义一个嵌套的泛型、子泛型
* */
static class ParametricTypes {
/**
* 泛型1
* */
private Class clazz;
/**
* 子泛型
* */
private List<ParametricTypes> subClazz;
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public List<ParametricTypes> getSubClazz() {
return subClazz;
}
public void setSubClazz(List<ParametricTypes> subClazz) {
this.subClazz = subClazz;
}
}
/**
* 嵌套一层泛型序列化
*/
public static <T> T fromJson(String json, Class mainClass, Class subClass) {
try {
JavaType javaType = getMapper().getTypeFactory().constructParametricType(mainClass, subClass);
return getMapper().readValue(json, javaType);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 嵌套泛型序列化
*/
public static <T> T fromJson(String json, ParametricTypes parametricTypes) {
try {
// getMapper().getTypeFactory().constructParametricType()
JavaType javaType = getJavaType(parametricTypes);
return getMapper().readValue(json, javaType);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
// return null;
}
private static JavaType getJavaType(ParametricTypes parametricTypes) {
JavaType javaType = null;
Class clazz = parametricTypes.getClazz();
List<ParametricTypes> subClazz = parametricTypes.getSubClazz();
if (CollectionUtils.isEmpty(subClazz)) {
Class[] classes = new Class[0];
javaType = getMapper().getTypeFactory().constructParametricType(clazz, classes);
} else {
JavaType[] javaTypes = new JavaType[subClazz.size()];
for (int i = 0; i < subClazz.size(); i++) {
JavaType jt = getJavaType(subClazz.get(i));
javaTypes[i] = jt;
}
javaType = getMapper().getTypeFactory().constructParametricType(clazz, javaTypes);
}
return javaType;
}
private static ObjectMapper getMapper() {
if (mapper == null) {
mapper = new ObjectMapper();
/*ObjectMapper configure = mapper
.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,
true);*/
//为null的不输出
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//大小写问题
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
//设置等同于@JsonIgnoreProperties(ignoreUnknown = true)
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);//防止转为json是首字母大写的属性会出现两次
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//设置JSON时间格式
SimpleDateFormat myDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mapper.setDateFormat(myDateFormat);
// mapper.configure(SerializationFeature.WRAP_ROOT_VALUE CLOSE_CLOSEABLE)
}
return mapper;
}
/**
* 单位缩进字符串。
*/
private static String SPACE = "\t";
/**
* 返回格式化JSON字符串。
*
* @param json 未格式化的JSON字符串。
* @return 格式化的JSON字符串。
*/
public static String formatJson(String json)
{
StringBuffer result = new StringBuffer();
int length = json.length();
int number = 0;
char key = 0;
//遍历输入字符串。
for (int i = 0; i < length; i++)
{
//1、获取当前字符。
key = json.charAt(i);
//2、如果当前字符是前方括号、前花括号做如下处理:
if((key == '[') || (key == '{') )
{
//(1)如果前面还有字符,并且字符为“:”,打印:换行和缩进字符字符串。
if((i - 1 > 0) && (json.charAt(i - 1) == ':'))
{
result.append('\n');
result.append(indent(number));
}
//(2)打印:当前字符。
result.append(key);
//(3)前方括号、前花括号,的后面必须换行。打印:换行。
result.append('\n');
//(4)每出现一次前方括号、前花括号;缩进次数增加一次。打印:新行缩进。
number++;
result.append(indent(number));
//(5)进行下一次循环。
continue;
}
//3、如果当前字符是后方括号、后花括号做如下处理:
if((key == ']') || (key == '}') )
{
//(1)后方括号、后花括号,的前面必须换行。打印:换行。
result.append('\n');
//(2)每出现一次后方括号、后花括号;缩进次数减少一次。打印:缩进。
number--;
result.append(indent(number));
//(3)打印:当前字符。
result.append(key);
//(4)如果当前字符后面还有字符,并且字符不为“,”,打印:换行。
if(((i + 1) < length) && (json.charAt(i + 1) != ','))
{
result.append('\n');
}
//(5)继续下一次循环。
continue;
}
//4、如果当前字符是逗号。逗号后面换行,并缩进,不改变缩进次数。
if((key == ','))
{
result.append(key);
result.append('\n');
result.append(indent(number));
continue;
}
//5、打印:当前字符。
result.append(key);
}
return result.toString();
}
/**
* 返回指定次数的缩进字符串。每一次缩进三个空格,即SPACE。
*
* @param number 缩进次数。
* @return 指定缩进次数的字符串。
*/
private static String indent(int number)
{
StringBuffer result = new StringBuffer();
for(int i = 0; i < number; i++)
{
result.append(SPACE);
}
return result.toString();
}
/**
*
* 校验一个JSON串是否为JSON结构,必须满足Map或集合结构
* */
public static boolean validateJson(String json) {
boolean flag = false;
try {
JsonUtils.fromJson(json, Map.class);
return true;
} catch (Exception e) {
}
try {
JsonUtils.fromJson(json, List.class);
return true;
} catch (Exception e) {
}
return flag;
}
/**
* 向JSON中追加参数
* 注意:只支持Map类型的JSON
*
* @param json 原始JSON字符串。
* @param data 要添加的数据,数组类型,数组里两个值,第一个值为key,第二个值为value
* @return 追加后的JSON字符串。
*/
public static String appendJson(String json, Object[] ... data) throws RuntimeException {
Map map = null;
try {
map = JsonUtils.fromJson(json, Map.class);
} catch (Exception e) {
throw new RuntimeException("JSON格式错误,只支持Map格式的JSON", e);
}
if (data != null) {
for (Object[] datum : data) {
if (datum == null || datum.length != 2) {
throw new RuntimeException("传入的追加格式错误");
}
map.put(datum[0], datum[1]);
}
}
return toJson(map);
}
public static JsonArray createJsonArray() {
return new JsonArray();
}
public static JsonObject createJsonObject() {
return new JsonObject();
}
public static Object convertValue(Object value) {
Object convertData = null;
if (value instanceof JsonObject) {
JsonObject jo = (JsonObject) value;
convertData = jo.toMap();
} else if (value instanceof JsonArray) {
JsonArray jsonArray = (JsonArray) value;
convertData = jsonArray.toList();
} else if (value instanceof List) {
String s = JsonUtils.toJson(value);
convertData = JsonUtils.fromJson(s, List.class);
} else if (value instanceof Integer ||
value instanceof Double ||
value instanceof Character ||
value instanceof Float ||
value instanceof Short ||
value instanceof Byte ||
value instanceof Boolean ||
value instanceof String ||
value instanceof Long) {
//基本数据类型不做处理
convertData = value;
} else {
//Map 和JavaBean都转为Map
//注意这样不能保证Map的顺序
try {
String s = JsonUtils.toJson(value);
convertData = JsonUtils.fromJson(s, Map.class);
} catch (Exception e) {
throw new RuntimeException("转化出错,不能转为Map结构", e);
}
}
return convertData;
}
/**
*
* 获取JSON中的一个数据,字符串形式
* */
public static String getValueByKey(String json, String key) {
return JsonCode.getValue(json, String.format("$.%s", key));
}
}
package com.iscas.templet.common;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: zhuquanwen
* @Description:
* @Date: 2017/12/25 16:41
* @Modified:
**/
@Data
@ToString(callSuper = true)
@Accessors(chain = true)
public class ResponseEntity<T> implements Serializable {
/**
* http状态码
*/
// protected Integer status;
/**
* 状态信息
*/
protected String message;
/**
* 服务器内部错误描述
*/
protected String desc;
/**
* 返回值
*/
protected T value;
/**
* 访问URL
*/
protected String requestURL;
protected long tookInMillis;
protected int total;
public ResponseEntity(Integer status, String message) {
super();
// this.status = status;
this.message = message;
}
public ResponseEntity() {
super();
// this.status = 200;
this.message = "操作成功";
}
public ResponseEntity(String message){
super();
this.message = message;
}
}
15、启动类
package com.iscas.biz.security;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/2/27 15:09
* @since jdk1.8
*/
@SpringBootApplication
public class BizSecurity extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(BizSecurity.class);
springApplication.run(args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(BizSecurity.class);
}
}
16、配置文件
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.database=mysql
spring.jpa.show-sql=true
17、测试
(1)未登录的情况访问/admin/details和/admin2/details
提示未登录
(2)未登录的情况访问/admin3/details
因为admin3不做权限控制,可以访问
(3)登录
使用spring security默认的登录接口,表单传入username和password,如我预期,生成了token,并返回,前端可以将此token存入localstorage
(4)登录后访问/admin/detail
在header中携带登录返回的token
(5)登录后访问/admin2/detail
因为在WebSecurityConfig的配置中,/admin2/**需要AAA的角色,而admin没有此角色,固鉴权失败,并返回了提示的JSON,而不是页面
另外,资源权限的配置提供一种方式:
在WebSecurityConfig中将硬代码的配置去掉,打开access,如下图
创建access中对应的类和函数
package com.iscas.biz.security.service;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.Set;
@Component("rbacauthorityservice")
public class RbacAuthorityService {
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Object userInfo = authentication.getPrincipal();
boolean hasPermission = false;
if (userInfo instanceof UserDetails) {
String username = ((UserDetails) userInfo).getUsername();
//获取资源
Set<String> urls = new HashSet();
urls.add("/common/**"); // 这些 url 都是要登录后才能访问,且其他的 url 都不能访问!
AntPathMatcher antPathMatcher = new AntPathMatcher();
for (String url : urls) {
if (antPathMatcher.match(url, request.getRequestURI())) {
hasPermission = true;
break;
}
}
return hasPermission;
} else {
return false;
}
}
}