文学类网站怎么做,普通网站报价多少,动漫设计招聘信息,seo点击文章目录 前言一、登录认证1. 问题引入2. Session2.1 实现原理2.2 过滤器Filter2.3 上下文对象 3. JWT3.2 实现步骤3.3 拦截器 HandlerInterceptorAdapter3.4 上下文对象 4. Session VS JWT 二、权限授权1. 权限类型1.1 页面权限#xff08;菜单项权限#xff09;1.2 ACL模型… 文章目录 前言一、登录认证1. 问题引入2. Session2.1 实现原理2.2 过滤器Filter2.3 上下文对象 3. JWT3.2 实现步骤3.3 拦截器 HandlerInterceptorAdapter3.4 上下文对象 4. Session VS JWT 二、权限授权1. 权限类型1.1 页面权限菜单项权限1.2 ACL模型 Access Control List访问控制列表不推荐1.3 RBAC模型Role-Based Access Controller基于角色访问控制模型 1.2 接口权限操作权限或按钮权限1.3 数据权限 三、SpringSecurity1. 概述2. 登录认证2.1 了解各个组件2.2 JWTSpringSecurity 3. 权限授权 总结 前言
登录认证和权限授权是所有项目中必不可少的功能本篇文章首先将通过最简单的方式Session和JWT实现登录认证和权限授权然后再整合Springsecurity框架实现。 一、登录认证
登录认证简言之就是对用户的身份进行确认判断用户的账号和密码是否正确。
1. 问题引入
HTTP请求是一个无状态的协议每一次发送的请求都是独立的需要一种机制来记住用户登录过 需要凭证 怎么保存凭证 Session/JWT 原理都是token机制
前端发起登录认证请求后端登录验证通过返回给前端一个凭证前端发起新的请求时携带凭证
2. Session
有状态的管理机制 如果用户第一次访问某个服务器时服务器响应数据时会在响应头的Set-Cookie标识里将Session Id返回给浏览器浏览器就将标识中的数据存在Cookie中浏览器后续访问服务器就会携带Cookie。
2.1 实现原理 2.2 过滤器Filter
问题引入除了登录接口外我们其他接口都要在Controller层里做登录判断这太麻烦了。 解决使用过滤器拦截判断有没有登录登录就放行没登录结束请求
2.3 上下文对象
问题引入要在service层获取操作用户对象需要从Controller层传参过来太麻烦了。 解决通过SpringMVC提供的RequestContextHolder对象在程序任何地方获取到当前请求对象从而获取我们保存在HttpSession中的用户对象写一个上下文对象来实现这一功能。 然后在Service层直接调用我们写的方法就可以获取到用户对象
3. JWT
Json web token。无状态管理机制
可以将一段数据加密成一段字符串也可以从这字符串解密回数据可以对这个字符串进行校验比如有没有过期有没有被篡改
3.2 实现步骤
1、写一个JWT的工具类工具类就提供两个方法一个生成一个解析 2、工具类做好之后我们可以开始写登录接口登录的时候生成token 3、后续会话中用户访问其他接口时就可以校验token来判断其是否已经登录。 一般来说前端将token一般会放在请求头的Authorization项传递过来其格式一般为类型 token 调用其他接口的时候从请求头中获取token然后再通过JWT工具类解析token判断。
3.3 拦截器 HandlerInterceptorAdapter
如果每个接口都要手动判断一下用户有没有登录太麻烦了所以我们做一个统一处理。 拦截器类写好之后别忘了要使其生效这里我们直接让SpringBoot启动类实现WebMvcConfigurer接 口重写addInterceptors方法
3.4 上下文对象
在一个线程中横跨若干方法调用需要传递的对象我们通常称之为上下文Context JWT不像Session把用户信息直接存储起来所以JWT的上下文对象要靠我们自己来实现。 这个类专门存储JWT解析出来的用户信息。我们要用到ThreadLocal存储用户信息以防止线程冲突 通过上下文对象类如UserContext可以在程序的其他地方直接获取到数据。上下文对象.方法()
public final class UserContext {private static final ThreadLocalString user new ThreadLocalString();public static void add(String userName) {user.set(userName);}public static void remove() {user.remove();}/*** return 当前登录用户的用户名*/public static String getCurrentUserName() {return user.get();}
}4. Session VS JWT
Session是有状态的JWT是无状态的Session在服务端保存了用户信息而JWT在服务端没有保存任何信息。当前端携带Session Id到服务端时服务端要检查其对应的HttpSession中有没有保存用户信息保存了就代表登录了当使用JWT时服务端只需要对这个字符串进行校验校验通过就代表登录了
二、权限授权
对用户能否问某个资源进行确认。认证成功之后再确认能访问什么。 权限系统的设计第一步就是考虑要保护什么资源再接着思考如何保护这个资源。
1. 权限类型
1.1 页面权限菜单项权限
有权限的用户就会显示所有菜单无权限的用户就只会显示部分菜单 一个页面菜单对应一个URI地址当用户登录的时候判断这个用户拥有哪些页面权限自然 而然就知道要渲染出什么导航菜单。
1.2 ACL模型 Access Control List访问控制列表不推荐
数据库设计三张表格一张表用来存储用户信息一张表存储资源路径中间表用来映射用户和资源表的关系。 具体实现 前端给后端发请求后后端回返回数据给前端这些数据中就包含了用户能够访问的资源信息前端本地也存有一个映射字典字典里有资源的信息比如id对应哪个路径、名称等等前端拿到了用户的id后根据字典进行判断就可以做到相应的功能。
1.3 RBAC模型Role-Based Access Controller基于角色访问控制模型
很多用户的权限都是相同的通过封装一层角色信息实现角色和权限绑定用户和角色绑定。
1.2 接口权限操作权限或按钮权限
前后端分离的模式下后端在登录的时候将权限数据甩给前端后就再也不管了如果此时用户的权限发生变化是无法通知前端的并且数据存储在前端也容易被用户直接篡改所以很不安全。 例如没有这个删除权限的人就不会显示该按钮或者该按钮被禁用. 页面渲染不走后端但接口可必须得走后端只需要对每个接口进行一个权限判断。 实现方式
接口扫描 RequestMappingInfoHandlerMapping这个类可以拿到所有你声明的web接口信息 通过代码将接口信息批量添加到数据库标记一下哪些接口是否需要被权限管理注解形式接口扫描具体实现 使用SpringBoot提供的ApplicationRunner接口来进行处理重写该接口的方法会在程序启动时被 执行。拦截器 每一个权限安全判断都是写在方法内且这个逻辑判断代码都是一样的会有重复代码 获取请求的最佳匹配路径例如/API/user/test/{id}路径参数 拦截器中获取权限数据现在是直接查的数据库实际开发中一定要将权限数据存在缓存里如Redis
1.3 数据权限
不同用户所能访问到的数据不同。 1、硬编码通过编写sql语句不推荐 2、Mybatis拦截插件 提供了一个Interceptor接口实现该接口定义一个拦截器对sql语句进行拦截达到数据过滤效果
三、SpringSecurity
1. 概述
Web系统中登录认证Authentication的核心就是凭证机制无论是Session还是JWT都是在用户成功登录时返回给用户一个凭证后续用户访问接口需携带凭证来标明自己的身份。权限授权Authorization是对用户能否访问某个资源进行确认这种通用逻辑都是放在过滤器里进行的统一操作。 Spring Security对Web系统的支持就是基于这一个个过滤器组成的过滤器链 Spring Security的核心逻辑全在这一套过滤器中过滤器里会调用各种组件完成功能掌握了这些过滤器和组件就掌握了Spring Security。 使用Spring Security肯定是要先引入依赖包
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId
/dependency2. 登录认证
2.1 了解各个组件
了解各个组件实现简单的登入认证基于Session机制。 确认当前是哪个用户正在使用我们系统就是登录认证的最终目的 这一概念在Spring Security中的体现就是 Authentication它存储了认证信息代表当前登录用户。 通过 SecurityContext上下文对象 来获取Authentication 上下文对象则是交由 **SecurityContextHolder **进行管理用来在程序任何地方获取SecurityContext
Authentication authentication SecurityContextHolder.getContext().getAuthentication()认证信息 Principal用户信息没有认证时一般是用户名认证后一般是用户对象 Credentials用户凭证一般是密码 Authorities用户权限 认证流程 登录接口代码
RestController
RequestMapping(/API)
public class LoginController {Autowiredprivate AuthenticationManager authenticationManager;PostMapping(/login)public String login(RequestBody LoginParam param) {// 生成一个包含账号密码的认证信息Authentication token new UsernamePasswordAuthenticationToken(param.getUsername(), param.getPassword());// AuthenticationManager校验这个认证信息返回一个已认证的AuthenticationAuthentication authentication authenticationManager.authenticate(token);// 将返回的Authentication存到上下文中SecurityContextHolder.getContext().setAuthentication(authentication);return 登录成功;}
}AuthenticationManager认证方式 Spring Security用于执行身份验证的组件只需要调用它的authenticate方法即可完成认证 默认的认证方式就是在UsernamePasswordAuthenticationFilter这个过滤器中调用这个组件该过滤器 负责认证逻辑。 加密器PasswordEncoder重写 实现此接口定义自己的加密规则和校验规则或者采用Spring Security提供的加密器实现
public interface PasswordEncoder {//加密String encode(CharSequence rawPassword);//将未加密的字符串前端传递过来的密码和已加密的字符串数据库中存储的密码进行校验boolean matches(CharSequence rawPassword, String encodedPassword);
}配置类里配置
Bean
public PasswordEncoder passwordEncoder() {// bcrypt加密算法安全性比较高return new BCryptPasswordEncoder();
}往数据库中添加用户数据时就要将密码进行加密否则后续进行密码校验时从数据库拿出来的还是明文密码。 实现用户注册接口
Autowired
private PasswordEncoder passwordEncoder;
PostMapping(/register)
public String register(RequestBody UserParam param) {UserEntity user new UserEntity();// 调用加密器将前端传递过来的密码进行加密user.setUsername(param.getUsername()).setPassword(passwordEncoder.encode(param.getPassword()));// 将用户实体对象添加到数据库userService.save(user);return 注册成功;
}用户对象UserDetails 重写 提供了用户的一些通用属性
public interface UserDetails extends Serializable {// 用户权限集合Collection? extends GrantedAuthority getAuthorities();// 用户密码String getPassword();// 用户名String getUsername();// 用户没过期返回true反之则falseboolean isAccountNonExpired();//......
}以上默认属性满足不了实际开发需求可以继承Spring Security提供org.springframework.security.core.userdetails.User类该类实现了 UserDetails接口帮我们省去了重写方法的工作
public class UserDetail extends User {//我们自己的用户实体对象要调取用户信息时直接获取这个实体对象。private UserEntity userEntity;public UserDetail(UserEntity userEntity, Collection? extends GrantedAuthority authorities) {// 必须调用父类的构造方法以初始化用户名、密码、权限super(userEntity.getUsername(), userEntity.getPassword(), authorities);this.userEntity userEntity;}
}业务对象UserDetailsService重写
public interface UserDetailsService {//根据用户名获取用户对象获取不到直接抛异常UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}可以自己定义逻辑
Service
public class UserServiceImpl implements UserService, UserDetailsService {Autowiredprivate UserMapper userMapper;Overridepublic UserDetails loadUserByUsername(String username) {// 从数据库中查询出用户实体对象UserEntity user userMapper.selectByUsername(username);// 若没查询到一定要抛出该异常这样才能被Spring Security的错误处理器处理if (user null) {throw new UsernameNotFoundException(没有找到该用户);}// 走到这代表查询到了实体对象那就返回我们自定义的UserDetail对象这里权限暂时放个空集合后面我会讲解return new UserDetail(user, Collections.emptyList());}
}认证异常处理器AuthenticationEntryPoint接口 当我们查询用户失败时或者校验密码失败时都会抛出Spring Security的自定义异常。这些异常不可能放任不管Spring Security对于这些异常都是在ExceptionTranslationFilter过滤器中进行处理AuthenticationEntryPoint则专门处理认证异常。 也可以自定义一个类实现AuthenticationEntryPoint接口从而实现我们自己的错误处理逻辑
public class MyEntryPoint implements AuthenticationEntryPoint {Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {response.setContentType(application/json;charsetutf-8);PrintWriter out response.getWriter();// 直接提示前端认证错误out.write(认证错误);out.flush();out.close();}
}配置 Spring Security对哪些接口进行保护、什么组件生效、某些功能是否启用等等都需要在配置类中进行配置
EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {....BeanOverrideprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}** 实现流程**
用户进行登录操作传递账号密码过来 登录接口调用AuthenticationManager根据用户名查询出用户数据 UserDetailService查询出UserDetails将传递过来的密码和数据库中的密码进行对比校验 PasswordEncoder校验通过则将认证信息存入到上下文中 将UserDetails存入到Authentication将Authentication存入到SecurityContext如果认证失败则抛出异常 由AuthenticationEntryPoint处理 基于session认证认证后Spring Security会将Authentication存入到session中不推荐
2.2 JWTSpringSecurity
1、当用户登录成功的同时我们需要生成token并返回给前端这样前端才能访问其他接口时携带token。
Autowired
private UserService userService;
PostMapping(/login)
public UserVO login(RequestBody Validated LoginParam user) {// 调用业务层执行登录操作return userService.login(user);
}service层实现login方法
public UserVO login(LoginParam param) {// 根据用户名查询出用户实体对象UserEntity user userMapper.selectByUsername(param.getUsername());// 若没有查到用户 或者 密码校验失败则抛出自定义异常if (user null || !passwordEncoder.matches(param.getPassword(), user.getPassword())) {throw new ApiException(账号密码错误);}// 需要返回给前端的VO对象UserVO userVO new UserVO();userVO.setId(user.getId()).setUsername(user.getUsername())// 生成JWT将用户名数据存入其中.setToken(jwtManager.generate(user.getUsername()));return userVO;
}2、登录成功返回token后续我们再访问其它接口时需要将token放到请求头中。这里我们需要自定义一个认证过滤器来对token进行校验。每当一个请求来时我们都会校验JWT进行认证上下文对象中有了Authentication后续过滤器就会知道该请求已经认证过了。
Component
public class LoginFilter extends OncePerRequestFilter {Autowiredprivate JwtManager jwtManager;Autowiredprivate UserServiceImpl userService;Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {// 从请求头中获取token字符串并解析Claims claims jwtManager.parse(request.getHeader(Authorization));if (claims ! null) {// 从JWT中提取出之前存储好的用户名String username claims.getSubject();// 查询出用户对象UserDetails user userService.loadUserByUsername(username);// 手动组装一个认证对象Authentication authentication new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());// 将认证对象放到上下文中SecurityContextHolder.getContext().setAuthentication(authentication);}chain.doFilter(request, response);}
}3、自定义的认证过滤器需要插入到默认的认证过滤器之前这样我们的过滤器才能生效。
http.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class);3. 权限授权
接口权限授权流程
当一个请求过来我们先得知道这个请求的规则即需要怎样的权限才能访问然后获取当前登录用户所拥有的权限再校验当前用户是否拥有该请求的权限用户拥有这个权限则正常返回数据没有权限则拒绝请求
Spring Security的授权发生在FilterSecurityInterceptor过滤器中步骤如下
首先调用的是 SecurityMetadataSource来获取当前请求的鉴权规则然后通过Authentication获取当前登录用户所有权限数据 GrantedAuthority认证对象里存放这权限数据再调用 AccessDecisionManager 来校验当前用户是否拥有该权限如果有就放行接口没有则抛出异常该异常会被 授权错误处理器AccessDeniedHandler 处理定义好以上组件就是写一个过滤器进行属性配置让组件生效回到Spring Security配置类让这个过滤器替换掉原有的过滤器 总结
文章中内容均来源于https://zhuanlan.zhihu.com/p/342755411原文作者讲解得十分透彻不仅让我理解了技术框架的使用方法还对框架中的源码为什么要这样去做有了更深入的认识这篇文章是对原文作者写的三篇文章的整合简化了很多方便本人之后复习巩固。 文章转载自: http://www.morning.tqsgt.cn.gov.cn.tqsgt.cn http://www.morning.ndxrm.cn.gov.cn.ndxrm.cn http://www.morning.jqtb.cn.gov.cn.jqtb.cn http://www.morning.pyswr.cn.gov.cn.pyswr.cn http://www.morning.mtmph.cn.gov.cn.mtmph.cn http://www.morning.rdgb.cn.gov.cn.rdgb.cn http://www.morning.tlrxp.cn.gov.cn.tlrxp.cn http://www.morning.nmfml.cn.gov.cn.nmfml.cn http://www.morning.rpjr.cn.gov.cn.rpjr.cn http://www.morning.mzqhb.cn.gov.cn.mzqhb.cn http://www.morning.yqkmd.cn.gov.cn.yqkmd.cn http://www.morning.pkpqh.cn.gov.cn.pkpqh.cn http://www.morning.nxcgp.cn.gov.cn.nxcgp.cn http://www.morning.flfdm.cn.gov.cn.flfdm.cn http://www.morning.thbqp.cn.gov.cn.thbqp.cn http://www.morning.lfqnk.cn.gov.cn.lfqnk.cn http://www.morning.rtlg.cn.gov.cn.rtlg.cn http://www.morning.tqbqb.cn.gov.cn.tqbqb.cn http://www.morning.pwxkn.cn.gov.cn.pwxkn.cn http://www.morning.hbywj.cn.gov.cn.hbywj.cn http://www.morning.gtylt.cn.gov.cn.gtylt.cn http://www.morning.rxwfg.cn.gov.cn.rxwfg.cn http://www.morning.zbpqq.cn.gov.cn.zbpqq.cn http://www.morning.pynzj.cn.gov.cn.pynzj.cn http://www.morning.fbzdn.cn.gov.cn.fbzdn.cn http://www.morning.yckwt.cn.gov.cn.yckwt.cn http://www.morning.fwkpp.cn.gov.cn.fwkpp.cn http://www.morning.ckbmz.cn.gov.cn.ckbmz.cn http://www.morning.dkbgg.cn.gov.cn.dkbgg.cn http://www.morning.qhqgk.cn.gov.cn.qhqgk.cn http://www.morning.wcqxj.cn.gov.cn.wcqxj.cn http://www.morning.nnrqg.cn.gov.cn.nnrqg.cn http://www.morning.srjgz.cn.gov.cn.srjgz.cn http://www.morning.tddrh.cn.gov.cn.tddrh.cn http://www.morning.gyrdn.cn.gov.cn.gyrdn.cn http://www.morning.ffhlh.cn.gov.cn.ffhlh.cn http://www.morning.jkpnm.cn.gov.cn.jkpnm.cn http://www.morning.ngcbd.cn.gov.cn.ngcbd.cn http://www.morning.nnpwg.cn.gov.cn.nnpwg.cn http://www.morning.jzlfq.cn.gov.cn.jzlfq.cn http://www.morning.kcrw.cn.gov.cn.kcrw.cn http://www.morning.hmnhp.cn.gov.cn.hmnhp.cn http://www.morning.nyqb.cn.gov.cn.nyqb.cn http://www.morning.mtgnd.cn.gov.cn.mtgnd.cn http://www.morning.rswfj.cn.gov.cn.rswfj.cn http://www.morning.qxmpp.cn.gov.cn.qxmpp.cn http://www.morning.wnwjf.cn.gov.cn.wnwjf.cn http://www.morning.yrngx.cn.gov.cn.yrngx.cn http://www.morning.thpzn.cn.gov.cn.thpzn.cn http://www.morning.tldhq.cn.gov.cn.tldhq.cn http://www.morning.jppb.cn.gov.cn.jppb.cn http://www.morning.bsqkt.cn.gov.cn.bsqkt.cn http://www.morning.rwlsr.cn.gov.cn.rwlsr.cn http://www.morning.gdpai.com.cn.gov.cn.gdpai.com.cn http://www.morning.rxkl.cn.gov.cn.rxkl.cn http://www.morning.zxfdq.cn.gov.cn.zxfdq.cn http://www.morning.ndlww.cn.gov.cn.ndlww.cn http://www.morning.rlhgx.cn.gov.cn.rlhgx.cn http://www.morning.xkyst.cn.gov.cn.xkyst.cn http://www.morning.qzpkr.cn.gov.cn.qzpkr.cn http://www.morning.tcxzn.cn.gov.cn.tcxzn.cn http://www.morning.ldsgm.cn.gov.cn.ldsgm.cn http://www.morning.jbshh.cn.gov.cn.jbshh.cn http://www.morning.sltfk.cn.gov.cn.sltfk.cn http://www.morning.kgnnc.cn.gov.cn.kgnnc.cn http://www.morning.bhmnp.cn.gov.cn.bhmnp.cn http://www.morning.qtbnm.cn.gov.cn.qtbnm.cn http://www.morning.msgrq.cn.gov.cn.msgrq.cn http://www.morning.smpmn.cn.gov.cn.smpmn.cn http://www.morning.tcfhs.cn.gov.cn.tcfhs.cn http://www.morning.rpstb.cn.gov.cn.rpstb.cn http://www.morning.xmwdt.cn.gov.cn.xmwdt.cn http://www.morning.mzhh.cn.gov.cn.mzhh.cn http://www.morning.tpnx.cn.gov.cn.tpnx.cn http://www.morning.tnjz.cn.gov.cn.tnjz.cn http://www.morning.wztlr.cn.gov.cn.wztlr.cn http://www.morning.qsdnt.cn.gov.cn.qsdnt.cn http://www.morning.mldrd.cn.gov.cn.mldrd.cn http://www.morning.wlqll.cn.gov.cn.wlqll.cn http://www.morning.spwln.cn.gov.cn.spwln.cn