国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實(shí)現(xiàn)

瀏覽:17日期:2023-05-22 17:39:39

廢話(huà)

目前流行的前后端分離讓Java程序員可以更加專(zhuān)注的做好后臺(tái)業(yè)務(wù)邏輯的功能實(shí)現(xiàn),提供如返回Json格式的數(shù)據(jù)接口就可以。SpringBoot的易用性和對(duì)其他框架的高度集成,用來(lái)快速開(kāi)發(fā)一個(gè)小型應(yīng)用是最佳的選擇。

一套前后端分離的后臺(tái)項(xiàng)目,剛開(kāi)始就要面對(duì)的就是登陸和授權(quán)的問(wèn)題。這里提供一套方案供大家參考。

主要看點(diǎn):

登陸后獲取token,根據(jù)token來(lái)請(qǐng)求資源 根據(jù)用戶(hù)角色來(lái)確定對(duì)資源的訪問(wèn)權(quán)限 統(tǒng)一異常處理 返回標(biāo)準(zhǔn)的Json格式數(shù)據(jù)

正文

首先是pom文件:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--這是不是必須,只是我引用了里面一些類(lèi)的方法--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-solr</artifactId> </dependency><!--這是不是必須,只是我引用了里面一些類(lèi)的方法--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.6.1</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.6.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> <version>1.0.9.RELEASE</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

application.yml:

spring : datasource : url : jdbc:mysql://127.0.0.1:3306/les_data_center?useUnicode=true&amp;characterEncoding=UTF-8&allowMultiQueries=true&useAffectedRows=true&useSSL=false username : root password : 123456 driverClassName : com.mysql.jdbc.Driver jackson: data-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8mybatis : config-location : classpath:/mybatis-config.xml# JWTjwt: header: Authorization secret: mySecret #token有效期一天 expiration: 86400 tokenHead: 'Bearer '

接著是對(duì)security的配置,讓security來(lái)保護(hù)我們的API

SpringBoot推薦使用配置類(lèi)來(lái)代替xml配置。那這里,我也使用配置類(lèi)的方式。

@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final JwtAuthenticationEntryPoint unauthorizedHandler; private final AccessDeniedHandler accessDeniedHandler; private final UserDetailsService CustomUserDetailsService; private final JwtAuthenticationTokenFilter authenticationTokenFilter; @Autowired public WebSecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler, @Qualifier('RestAuthenticationAccessDeniedHandler') AccessDeniedHandler accessDeniedHandler, @Qualifier('CustomUserDetailsService') UserDetailsService CustomUserDetailsService, JwtAuthenticationTokenFilter authenticationTokenFilter) { this.unauthorizedHandler = unauthorizedHandler; this.accessDeniedHandler = accessDeniedHandler; this.CustomUserDetailsService = CustomUserDetailsService; this.authenticationTokenFilter = authenticationTokenFilter; } @Autowired public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder// 設(shè)置UserDetailsService.userDetailsService(this.CustomUserDetailsService)// 使用BCrypt進(jìn)行密碼的hash.passwordEncoder(passwordEncoder()); } // 裝載BCrypt密碼編碼器 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler).and()// 由于使用的是JWT,我們這里不需要csrf.csrf().disable().exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()// 基于token,所以不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 對(duì)于獲取token的rest api要允許匿名訪問(wèn).antMatchers('/api/v1/auth', '/api/v1/signout', '/error/**', '/api/**').permitAll()// 除上面外的所有請(qǐng)求全部需要鑒權(quán)認(rèn)證.anyRequest().authenticated(); // 禁用緩存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers('/v2/api-docs','/swagger-resources/configuration/ui','/swagger-resources','/swagger-resources/configuration/security','/swagger-ui.html' ); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }}

該類(lèi)中配置了幾個(gè)bean來(lái)供security使用。

JwtAuthenticationTokenFilter:token過(guò)濾器來(lái)驗(yàn)證token有效性 UserDetailsService:實(shí)現(xiàn)了DetailsService接口,用來(lái)做登陸驗(yàn)證 JwtAuthenticationEntryPoint :認(rèn)證失敗處理類(lèi) RestAuthenticationAccessDeniedHandler: 權(quán)限不足處理類(lèi)

那么,接下來(lái)一個(gè)一個(gè)實(shí)現(xiàn)這些類(lèi):

/** * token校驗(yàn),引用的stackoverflow一個(gè)答案里的處理方式 * Author: JoeTao * createAt: 2018/9/14 */@Componentpublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Value('${jwt.header}') private String token_header; @Resource private JWTUtils jwtUtils; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String auth_token = request.getHeader(this.token_header); final String auth_token_start = 'Bearer '; if (StringUtils.isNotEmpty(auth_token) && auth_token.startsWith(auth_token_start)) { auth_token = auth_token.substring(auth_token_start.length()); } else { // 不按規(guī)范,不允許通過(guò)驗(yàn)證 auth_token = null; } String username = jwtUtils.getUsernameFromToken(auth_token); logger.info(String.format('Checking authentication for user %s.', username)); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { User user = jwtUtils.getUserFromToken(auth_token); if (jwtUtils.validateToken(auth_token, user)) {UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));logger.info(String.format('Authenticated user %s, setting security context', username));SecurityContextHolder.getContext().setAuthentication(authentication); } } chain.doFilter(request, response); }}

/** * 認(rèn)證失敗處理類(lèi),返回401 * Author: JoeTao * createAt: 2018/9/20 */@Componentpublic class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { //驗(yàn)證為未登陸狀態(tài)會(huì)進(jìn)入此方法,認(rèn)證錯(cuò)誤 System.out.println('認(rèn)證失敗:' + authException.getMessage()); response.setStatus(200); response.setCharacterEncoding('UTF-8'); response.setContentType('application/json; charset=utf-8'); PrintWriter printWriter = response.getWriter(); String body = ResultJson.failure(ResultCode.UNAUTHORIZED, authException.getMessage()).toString(); printWriter.write(body); printWriter.flush(); }}

因?yàn)槲覀兪褂玫腞EST API,所以我們認(rèn)為到達(dá)后臺(tái)的請(qǐng)求都是正常的,所以返回的HTTP狀態(tài)碼都是200,用接口返回的code來(lái)確定請(qǐng)求是否正常。

/*** 權(quán)限不足處理類(lèi),返回403 * Author: JoeTao * createAt: 2018/9/21 */@Component('RestAuthenticationAccessDeniedHandler')public class RestAuthenticationAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException { //登陸狀態(tài)下,權(quán)限不足執(zhí)行該方法 System.out.println('權(quán)限不足:' + e.getMessage()); response.setStatus(200); response.setCharacterEncoding('UTF-8'); response.setContentType('application/json; charset=utf-8'); PrintWriter printWriter = response.getWriter(); String body = ResultJson.failure(ResultCode.FORBIDDEN, e.getMessage()).toString(); printWriter.write(body); printWriter.flush(); }}

/** * 登陸身份認(rèn)證 * Author: JoeTao * createAt: 2018/9/14 */@Component(value='CustomUserDetailsService')public class CustomUserDetailsService implements UserDetailsService { private final AuthMapper authMapper; public CustomUserDetailsService(AuthMapper authMapper) { this.authMapper = authMapper; } @Override public User loadUserByUsername(String name) throws UsernameNotFoundException { User user = authMapper.findByUsername(name); if (user == null) { throw new UsernameNotFoundException(String.format('No user found with username ’%s’.', name)); } Role role = authMapper.findRoleByUserId(user.getId()); user.setRole(role); return user; }}

登陸邏輯:

public ResponseUserToken login(String username, String password) { //用戶(hù)驗(yàn)證 final Authentication authentication = authenticate(username, password); //存儲(chǔ)認(rèn)證信息 SecurityContextHolder.getContext().setAuthentication(authentication); //生成token final User user = (User) authentication.getPrincipal();// User user = (User) userDetailsService.loadUserByUsername(username); final String token = jwtTokenUtil.generateAccessToken(user); //存儲(chǔ)token jwtTokenUtil.putToken(username, token); return new ResponseUserToken(token, user); }private Authentication authenticate(String username, String password) { try { //該方法會(huì)去調(diào)用userDetailsService.loadUserByUsername()去驗(yàn)證用戶(hù)名和密碼,如果正確,則存儲(chǔ)該用戶(hù)名密碼到“security 的 context中” return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); } catch (DisabledException | BadCredentialsException e) { throw new CustomException(ResultJson.failure(ResultCode.LOGIN_ERROR, e.getMessage())); } }

自定義異常:

@Getterpublic class CustomException extends RuntimeException{ private ResultJson resultJson; public CustomException(ResultJson resultJson) { this.resultJson = resultJson; }}

統(tǒng)一異常處理:

/** * 異常處理類(lèi) * controller層異常無(wú)法捕獲處理,需要自己處理 * Created by jt on 2018/8/27. */@RestControllerAdvice@Slf4jpublic class DefaultExceptionHandler { /** * 處理所有自定義異常 * @param e * @return */ @ExceptionHandler(CustomException.class) public ResultJson handleCustomException(CustomException e){ log.error(e.getResultJson().getMsg().toString()); return e.getResultJson(); }}

所有經(jīng)controller轉(zhuǎn)發(fā)的請(qǐng)求拋出的自定義異常都會(huì)被捕獲處理,一般情況下就是返回給調(diào)用方一個(gè)json的報(bào)錯(cuò)信息,包含自定義狀態(tài)碼、錯(cuò)誤信息及補(bǔ)充描述信息。

值得注意的是,在請(qǐng)求到達(dá)controller之前,會(huì)被Filter攔截,如果在controller或者之前拋出的異常,自定義的異常處理器是無(wú)法處理的,需要自己重新定義一個(gè)全局異常處理器或者直接處理。

Filter攔截請(qǐng)求兩次的問(wèn)題

跨域的post的請(qǐng)求會(huì)驗(yàn)證兩次,get不會(huì)。網(wǎng)上的解釋是,post請(qǐng)求第一次是預(yù)檢請(qǐng)求,Request Method: OPTIONS。解決方法:

在webSecurityConfig里添加

.antMatchers(HttpMethod.OPTIONS, '/**').permitAll()

就可以不攔截options請(qǐng)求了。

這里只給出了最主要的代碼,還有controller層的訪問(wèn)權(quán)限設(shè)置,返回狀態(tài)碼,返回類(lèi)定義等等。

所有代碼已上傳GitHub,項(xiàng)目地址

到此這篇關(guān)于SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot JWT 登陸鑒權(quán)內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 中文国产成人精品久久96 | 伊大人香蕉久久网 | 青青视频国产依人在线 | 加勒比毛片 | 在线观看日本亚洲一区 | 一区自拍| 久久精品综合国产二区 | 牛牛本精品99久久精品88m | 亚洲欧美日韩国产精品影院 | 成年人国产视频 | 男操女视频网站 | 国产短裙黑色丝袜在线观看下 | 男女男免费视频网站国产 | 国产高清在线观看视频手机版 | 女教师的一级毛片 | 一区二区三区久久精品 | 国产精品久久久久无毒 | 男女免费在线视频 | 免费一级特黄特色黄大任片 | 国产精品久久久久久久久久影院 | 黄在线看| 一区二区三区在线观看视频 | 国内精自品线一区91 | 最新亚洲一区二区三区四区 | 91伊人影院| 欧美xx在线观看 | 欧美一线高本道高清在线 | 国产理论在线观看 | 美女视频永久黄网站在线观看 | 日韩一级一欧美一级国产 | 久久国产精品免费看 | 特级毛片全部免费播放器 | 思思91精品国产综合在线 | 可以免费看黄的网站 | 亚洲欧美一区二区三区四区 | 久久观看视频 | 337p欧美| 久久久精品2018免费观看 | 99久久免费国产精精品 | 精品国产自在现线看久久 | 伊人青|