网站建设工具最简洁的,网站开发预算报表,网站建设 业务,基层建设杂志网站一、关于 SpringSecurity 在 Spring Boot 出现之前#xff0c;SpringSecurity 的使用场景是被另外一个安全管理框架 Shiro 牢牢霸占的#xff0c;因为相对于 SpringSecurity 来说#xff0c;SSM 中整合 Shiro 更加轻量级。Spring Boot 出现后#xff0c;使这一情况情况大有…一、关于 SpringSecurity 在 Spring Boot 出现之前SpringSecurity 的使用场景是被另外一个安全管理框架 Shiro 牢牢霸占的因为相对于 SpringSecurity 来说SSM 中整合 Shiro 更加轻量级。Spring Boot 出现后使这一情况情况大有改观。
这是因为 Spring Boot 为 SpringSecurity 提供了自动化配置大大降低了 SpringSecurity 的学习成本。另外SpringSecurity 的功能也比 Shiro 更加强大。 JWT目前最流行的一个跨域认证解决方案客户端发起用户登录请求服务器端接收并认证成功后生成一个 JSON 对象如下所示然后将其返回给客户端。 从本质上来说JWT 就像是一种生成加密用户身份信息的 Token更安全也更灵活。 三、整合步骤 第一步给需要登录认证的模块添加 codingmore-security 依赖 !-- 后端管理模块需要登录认证--dependencygroupIdtop.codingmore/groupIdartifactIdcodingmore-security/artifactIdversion1.0-SNAPSHOT/version 比如说 codingmore-admin 后端管理模块需要登录认证就在 codingmore-admin/pom.xml 文件中添加 codingmore-security 依赖。 第二步在需要登录认证的模块里添加 CodingmoreSecurityConfig 类继承自 codingmore-security 模块中的 SecurityConfig 类。 Configuration
EnableWebSecurity
EnableGlobalMethodSecurity(prePostEnabled true)
public class CodingmoreSecurityConfig extends CustomSecurityConfig {Autowiredprivate IUsersService usersService;Autowiredprivate IResourceService resourceService;Beanpublic UserDetailsService userDetailsService() {//获取登录用户信息return username - usersService.loadUserByUsername(username);}UserDetailsService 这个类主要是用来加载用户信息的包括用户名、密码、权限、角色集合....其中有一个方法如下 UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
认证逻辑中SpringSecurity 会调用这个方法根据客户端传入的用户名加载该用户的详细信息包括判断 密码是否一致通过后获取权限和角色 public UserDetails loadUserByUsername(String username) {// 根据用户名查询用户Users admin getAdminByUsername(username);if (admin ! null) {ListResource resourceList getResourceList(admin.getId());return new AdminUserDetails(admin,resourceList);}throw new UsernameNotFoundException(用户名或密码错误);}
getAdminByUsername 负责根据用户名从数据库中查询出密码、角色、权限等。 Overridepublic Users getAdminByUsername(String username) {// QueryWrapperUsers queryWrapper new QueryWrapper();//user_login为数据库字段// queryWrapper.eq(user_login, username);//ListUsers usersList baseMapper.selectList(queryWrapper);LambdaQueryWrapperUsers queryWrapper new LambdaQueryWrapper();queryWrapper.eq(Users::getUserLogin , username);ListUsers usersList baseMapper.selectList(queryWrapper);if (usersList ! null usersList.size() 0) {return usersList.get(0);}// 用户名错误提前抛出异常throw new UsernameNotFoundException(用户名错误);} 第三步在 application.yml 中配置下不需要安全保护的资源路径
secure:ignored:urls: #安全路径白名单- /doc.html- /swagger-ui/**- /swagger/**- /swagger-resources/**- /**/v3/api-docs- /**/*.js- /**/*.css- /**/*.png- /**/*.ico- /webjars/springfox-swagger-ui/**- /actuator/**- /druid/**- /users/login- /users/register- /users/info- /users/logout 第四步在登录接口中添加登录和刷新 token 的方法
Controller
Api(tags 用户)
RequestMapping(/users)
public class UsersController {Autowiredprivate IUsersService usersService;Value(${jwt.tokenHeader})private String tokenHeader;Value(${jwt.tokenHead})private String tokenHead;ApiOperation(value 登录以后返回token)RequestMapping(value /login, method RequestMethod.POST)ResponseBodypublic ResultObject login(Validated UsersLoginParam users, BindingResult result) {String token usersService.login(users.getUserLogin(), users.getUserPass());if (token null) {return ResultObject.validateFailed(用户名或密码错误);}// 将 JWT 传递回客户端MapString, String tokenMap new HashMap();tokenMap.put(token, token);tokenMap.put(tokenHead, tokenHead);return ResultObject.success(tokenMap);}ApiOperation(value 刷新token)RequestMapping(value /refreshToken, method RequestMethod.GET)ResponseBodypublic ResultObject refreshToken(HttpServletRequest request) {String token request.getHeader(tokenHeader);String refreshToken usersService.refreshToken(token);if (refreshToken null) {return ResultObject.failed(token已经过期);}MapString, String tokenMap new HashMap();tokenMap.put(token, refreshToken);tokenMap.put(tokenHead, tokenHead);return ResultObject.success(tokenMap);}
}
使用 Apipost 来测试一下首先是文章获取接口在没有登录的情况下会提示暂未登录或者 token 已过期。 四、实现原理
仅用四步就实现了登录认证主要是因为将 SpringSecurityJWT 的代码封装成了通用模块我们来看看 codingmore-security 的目录结构。 codingmore-security
├── component
| ├── JwtAuthenticationTokenFilter -- JWT登录授权过滤器
| ├── RestAuthenticationEntryPoint
| └── RestfulAccessDeniedHandler
├── config
| ├── IgnoreUrlsConfig
| └── SecurityConfig
└── util└── JwtTokenUtil -- JWT的token处理工具类
JwtAuthenticationTokenFilter 和 JwtTokenUtil 补充一下
客户端的请求头里携带了 token服务端肯定是需要针对每次请求解析校验 token 的所以必须得定义一个过滤器也就是 JwtAuthenticationTokenFilter 从请求头中获取 token对 token 进行解析、验签、校验过期时间校验成功将验证结果放到 ThreadLocal 中供下次请求使用 重点来看其他四个类。第一个 RestAuthenticationEntryPoint自定义返回结果未登录或登录过期 public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {response.setHeader(Access-Control-Allow-Origin, *);response.setHeader(Cache-Control,no-cache);response.setCharacterEncoding(UTF-8);response.setContentType(application/json);response.getWriter().println(JSONUtil.parse(ResultObject.unauthorized(authException.getMessage())));response.getWriter().flush();}
}
可以通过 debug 的方式看一下返回的信息正是之前用户未登录状态下访问文章页的错误信息。 具体的信息是在 ResultCode 类中定义的。
public enum ResultCode implements IErrorCode {SUCCESS(0, 操作成功),FAILED(500, 操作失败),VALIDATE_FAILED(506, 参数检验失败),UNAUTHORIZED(401, 暂未登录或token已经过期),FORBIDDEN(403, 没有相关权限);private long code;private String message;private ResultCode(long code, String message) {this.code code;this.message message;}
} 第二个 RestfulAccessDeniedHandler自定义返回结果没有权限访问时
public class RestfulAccessDeniedHandler implements AccessDeniedHandler{Overridepublic void handle(HttpServletRequest request,HttpServletResponse response,AccessDeniedException e) throws IOException, ServletException {response.setHeader(Access-Control-Allow-Origin, *);response.setHeader(Cache-Control,no-cache);response.setCharacterEncoding(UTF-8);response.setContentType(application/json);response.getWriter().println(JSONUtil.parse(ResultObject.forbidden(e.getMessage())));response.getWriter().flush();}
}
第三个IgnoreUrlsConfig用于配置不需要安全保护的资源路径
Getter
Setter
ConfigurationProperties(prefix secure.ignored)
public class IgnoreUrlsConfig {private ListString urls new ArrayList();
}
通过 lombok 注解的方式直接将配置文件中不需要权限校验的路径放开比如说 Knife4j 的接口文档页面。如果不放开的话就被 SpringSecurity 拦截了没办法访问到了。 第四个SecurityConfigSpringSecurity通用配置
public class SecurityConfig extends WebSecurityConfigurerAdapter {Autowired(required false)private DynamicSecurityService dynamicSecurityService;Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {ExpressionUrlAuthorizationConfigurerHttpSecurity.ExpressionInterceptUrlRegistry registry httpSecurity.authorizeRequests();//不需要保护的资源路径允许访问for (String url : ignoreUrlsConfig().getUrls()) {registry.antMatchers(url).permitAll();}// 任何请求需要身份认证registry.and().authorizeRequests().anyRequest().authenticated()// 关闭跨站请求防护及不使用session.and().csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)// 自定义权限拒绝处理类.and().exceptionHandling().accessDeniedHandler(restfulAccessDeniedHandler()).authenticationEntryPoint(restAuthenticationEntryPoint())// 自定义权限拦截器JWT过滤器.and().addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);//有动态权限配置时添加动态权限校验过滤器if(dynamicSecurityService!null){registry.and().addFilterBefore(dynamicSecurityFilter(), FilterSecurityInterceptor.class);}}
}
这个类的主要作用就是告诉 SpringSecurity 那些路径不需要拦截除此之外的都要进行 RestfulAccessDeniedHandler登录校验、RestAuthenticationEntryPoint权限校验和 JwtAuthenticationTokenFilterJWT 过滤。
并且将 JwtAuthenticationTokenFilter 过滤器添加到 UsernamePasswordAuthenticationFilter 过滤器之前。 五、测试 第一步测试登录接口Apipost 直接访问 http://localhost:9002/users/login可以看到 token 正常返回。 第二步不带 token 直接访问文章接口可以看到进入了 RestAuthenticationEntryPoint 这个处理器 第三步携带 token这次我们改用 Knife4j 来测试发现可以正常访问 文章转载自: http://www.morning.czgtt.cn.gov.cn.czgtt.cn http://www.morning.drggr.cn.gov.cn.drggr.cn http://www.morning.bpptt.cn.gov.cn.bpptt.cn http://www.morning.tntbs.cn.gov.cn.tntbs.cn http://www.morning.wrkhf.cn.gov.cn.wrkhf.cn http://www.morning.sqlh.cn.gov.cn.sqlh.cn http://www.morning.fppzc.cn.gov.cn.fppzc.cn http://www.morning.heleyo.com.gov.cn.heleyo.com http://www.morning.gpcy.cn.gov.cn.gpcy.cn http://www.morning.mbdbe.cn.gov.cn.mbdbe.cn http://www.morning.htqrh.cn.gov.cn.htqrh.cn http://www.morning.ltspm.cn.gov.cn.ltspm.cn http://www.morning.vjwkb.cn.gov.cn.vjwkb.cn http://www.morning.qptbn.cn.gov.cn.qptbn.cn http://www.morning.wrqw.cn.gov.cn.wrqw.cn http://www.morning.zcwzl.cn.gov.cn.zcwzl.cn http://www.morning.tygn.cn.gov.cn.tygn.cn http://www.morning.kngqd.cn.gov.cn.kngqd.cn http://www.morning.flncd.cn.gov.cn.flncd.cn http://www.morning.mcmpq.cn.gov.cn.mcmpq.cn http://www.morning.gsqw.cn.gov.cn.gsqw.cn http://www.morning.rqlbp.cn.gov.cn.rqlbp.cn http://www.morning.spwln.cn.gov.cn.spwln.cn http://www.morning.dygqq.cn.gov.cn.dygqq.cn http://www.morning.frmmp.cn.gov.cn.frmmp.cn http://www.morning.rhpgk.cn.gov.cn.rhpgk.cn http://www.morning.jwgnn.cn.gov.cn.jwgnn.cn http://www.morning.rgnp.cn.gov.cn.rgnp.cn http://www.morning.rdzlh.cn.gov.cn.rdzlh.cn http://www.morning.mtjwp.cn.gov.cn.mtjwp.cn http://www.morning.mpszk.cn.gov.cn.mpszk.cn http://www.morning.wmfh.cn.gov.cn.wmfh.cn http://www.morning.btqqh.cn.gov.cn.btqqh.cn http://www.morning.rycd.cn.gov.cn.rycd.cn http://www.morning.fdrwk.cn.gov.cn.fdrwk.cn http://www.morning.kpypy.cn.gov.cn.kpypy.cn http://www.morning.dbfp.cn.gov.cn.dbfp.cn http://www.morning.xlclj.cn.gov.cn.xlclj.cn http://www.morning.qkqjz.cn.gov.cn.qkqjz.cn http://www.morning.wdjcr.cn.gov.cn.wdjcr.cn http://www.morning.rdng.cn.gov.cn.rdng.cn http://www.morning.krkwh.cn.gov.cn.krkwh.cn http://www.morning.pkwwq.cn.gov.cn.pkwwq.cn http://www.morning.wjlnz.cn.gov.cn.wjlnz.cn http://www.morning.gxklx.cn.gov.cn.gxklx.cn http://www.morning.rxdsq.cn.gov.cn.rxdsq.cn http://www.morning.prhfc.cn.gov.cn.prhfc.cn http://www.morning.mjxgs.cn.gov.cn.mjxgs.cn http://www.morning.jnzfs.cn.gov.cn.jnzfs.cn http://www.morning.wrbx.cn.gov.cn.wrbx.cn http://www.morning.ytbr.cn.gov.cn.ytbr.cn http://www.morning.rxhs.cn.gov.cn.rxhs.cn http://www.morning.skrh.cn.gov.cn.skrh.cn http://www.morning.ytnn.cn.gov.cn.ytnn.cn http://www.morning.bpkqd.cn.gov.cn.bpkqd.cn http://www.morning.qhvah.cn.gov.cn.qhvah.cn http://www.morning.yyzgl.cn.gov.cn.yyzgl.cn http://www.morning.frqtc.cn.gov.cn.frqtc.cn http://www.morning.hsjfs.cn.gov.cn.hsjfs.cn http://www.morning.khlxd.cn.gov.cn.khlxd.cn http://www.morning.dyrzm.cn.gov.cn.dyrzm.cn http://www.morning.gxcit.com.gov.cn.gxcit.com http://www.morning.pqbkk.cn.gov.cn.pqbkk.cn http://www.morning.wchcx.cn.gov.cn.wchcx.cn http://www.morning.yrmgh.cn.gov.cn.yrmgh.cn http://www.morning.sgbjh.cn.gov.cn.sgbjh.cn http://www.morning.wklhn.cn.gov.cn.wklhn.cn http://www.morning.rwmft.cn.gov.cn.rwmft.cn http://www.morning.rdnkx.cn.gov.cn.rdnkx.cn http://www.morning.nytqy.cn.gov.cn.nytqy.cn http://www.morning.thjqk.cn.gov.cn.thjqk.cn http://www.morning.rdnjc.cn.gov.cn.rdnjc.cn http://www.morning.ypzr.cn.gov.cn.ypzr.cn http://www.morning.qhrdx.cn.gov.cn.qhrdx.cn http://www.morning.pnmtk.cn.gov.cn.pnmtk.cn http://www.morning.ftznb.cn.gov.cn.ftznb.cn http://www.morning.c7491.cn.gov.cn.c7491.cn http://www.morning.qyjqj.cn.gov.cn.qyjqj.cn http://www.morning.zsthg.cn.gov.cn.zsthg.cn http://www.morning.xqffq.cn.gov.cn.xqffq.cn