day18項(xiàng)目【權(quán)限管理和配置服務(wù)】-01 整合Spring Security

01-整合Spring Security

一、Spring Security介紹

1、框架介紹

Spring?是一個(gè)非常流行和成功的Java應(yīng)用開(kāi)發(fā)框架。Spring Security?基于?Spring?框架,提供了一套?Web?應(yīng)用安全性的完整解決方案。一般來(lái)說(shuō),Web?應(yīng)用的安全性包括用戶認(rèn)證Authentication)和用戶授權(quán)(Authorization兩個(gè)部分。

(1)用戶認(rèn)證指的是:驗(yàn)證某個(gè)用戶是否為系統(tǒng)中的合法主體,也就是說(shuō)用戶能否訪問(wèn)該系統(tǒng)。用戶認(rèn)證一般要求用戶提供用戶名和密碼。系統(tǒng)通過(guò)校驗(yàn)用戶名和密碼來(lái)完成認(rèn)證過(guò)程。

(2)用戶授權(quán)指的是驗(yàn)證某個(gè)用戶是否有權(quán)限執(zhí)行某個(gè)操作。在一個(gè)系統(tǒng)中,不同用戶所具有的權(quán)限是不同的。比如對(duì)一個(gè)文件來(lái)說(shuō),有的用戶只能進(jìn)行讀取,而有的用戶可以進(jìn)行修改。一般來(lái)說(shuō),系統(tǒng)會(huì)為不同的用戶分配不同的角色,而每個(gè)角色則對(duì)應(yīng)一系列的權(quán)限。

Spring Security其實(shí)就是用filter,多請(qǐng)求的路徑進(jìn)行過(guò)濾。

(1)如果是基于Session,那么Spring-security會(huì)對(duì)cookie里的sessionid進(jìn)行解析,找到服務(wù)器存儲(chǔ)的sesion信息,然后判斷當(dāng)前用戶是否符合請(qǐng)求的要求。

(2)如果是token,則是解析出token,然后將當(dāng)前請(qǐng)求加入到Spring-security管理的權(quán)限信息中去

2、認(rèn)證與授權(quán)實(shí)現(xiàn)思路

如果系統(tǒng)的模塊眾多,每個(gè)模塊都需要就行授權(quán)與認(rèn)證,所以我們選擇基于token的形式進(jìn)行授權(quán)與認(rèn)證,用戶根據(jù)用戶名密碼認(rèn)證成功,然后獲取當(dāng)前用戶角色的一系列權(quán)限值,并以用戶名為key,權(quán)限列表為value的形式存入redis緩存中,根據(jù)用戶名相關(guān)信息生成token返回,瀏覽器將token記錄到cookie中,每次調(diào)用api接口都默認(rèn)將token攜帶到header請(qǐng)求頭中,Spring-security解析header頭獲取token信息,解析token獲取當(dāng)前用戶名,根據(jù)用戶名就可以從redis中獲取權(quán)限列表,這樣Spring-security就能夠判斷當(dāng)前請(qǐng)求是否有權(quán)限訪問(wèn)

二、整合Spring Security

1、在common下創(chuàng)建spring_security模塊

2、在spring_security引入相關(guān)依賴

? ? <dependencies>

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>com.atguigu</groupId>

? ? ? ? ? ? <artifactId>common_utils</artifactId>

? ? ? ? ? ? <version>0.0.1-SNAPSHOT</version>

? ? ? ? </dependency>

? ? ? ? <!-- Spring Security依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.boot</groupId>

? ? ? ? ? ? <artifactId>spring-boot-starter-security</artifactId>

? ? ? ? </dependency>

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>io.jsonwebtoken</groupId>

? ? ? ? ? ? <artifactId>jjwt</artifactId>? //生成token需要使用到j(luò)wt

? ? ? ? </dependency>

? ? </dependencies>

3、在service_acls引入spring_security依賴

????????<dependency>

? ? ? ? ? ? <groupId>com.atguigu</groupId>

? ? ? ? ? ? <artifactId>spring_security</artifactId>

? ? ? ? ? ? <version>0.0.1-SNAPSHOT</version>

? ? ? ? </dependency>

4、復(fù)制工具類到common_utils

5、創(chuàng)建spring security核心配置類

Spring Security的核心配置就是繼承WebSecurityConfigurerAdapter并注解@EnableWebSecurity的配置。

這個(gè)配置指明了用戶名密碼的處理方式、請(qǐng)求路徑的開(kāi)合、登錄登出控制等和安全相關(guān)的配置。

package com.atguigu.serurity.config;

import com.atguigu.serurity.filter.TokenAuthenticationFilter;

import com.atguigu.serurity.filter.TokenLoginFilter;

import com.atguigu.serurity.security.DefaultPasswordEncoder;

import com.atguigu.serurity.security.TokenLogoutHandler;

import com.atguigu.serurity.security.TokenManager;

import com.atguigu.serurity.security.UnauthorizedEntryPoint;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

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.core.userdetails.UserDetailsService;

/**

* <p>

* Security配置類

* </p>

*

* @author qy

* @since 2019-11-18

*/

@Configuration? //表示配置類

@EnableWebSecurity

@EnableGlobalMethodSecurity(prePostEnabled = true)

public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {

? ? private UserDetailsService userDetailsService;

? ? private TokenManager tokenManager;

? ? private DefaultPasswordEncoder defaultPasswordEncoder;

? ? private RedisTemplate redisTemplate;


? ? @Autowired

? ? public TokenWebSecurityConfig(UserDetailsService userDetailsService, DefaultPasswordEncoder defaultPasswordEncoder,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TokenManager tokenManager, RedisTemplate redisTemplate) {

? ? ? ? this.userDetailsService = userDetailsService;

? ? ? ? this.defaultPasswordEncoder = defaultPasswordEncoder;

? ? ? ? this.tokenManager = tokenManager;

? ? ? ? this.redisTemplate = redisTemplate;

? ? }

? ? /**

? ? * 配置設(shè)置

? ? * @param http

? ? * @throws Exception

? ? */

? ? @Override

? ? protected void configure(HttpSecurity http) throws Exception {

? ? ? ? http.exceptionHandling()

? ? ? ? ? ? ? ? .authenticationEntryPoint(new UnauthorizedEntryPoint())

? ? ? ? ? ? ? ? .and().csrf().disable()

? ? ? ? ? ? ? ? .authorizeRequests()

? ? ? ? ? ? ? ? .anyRequest().authenticated()

????????????????//設(shè)置退出請(qǐng)求地址,這個(gè)地址是由spring security做到的,因此這個(gè)地址理論上可以隨便寫(xiě)

? ? ? ? ? ? ? ? .and().logout().logoutUrl("/admin/acl/index/logout")

? ? ? ? ? ? ? ? .addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate)).and()

? ? ? ? ? ? ? ? .addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate))

? ? ? ? ? ? ? ? .addFilter(new TokenAuthenticationFilter(authenticationManager(), tokenManager, redisTemplate)).httpBasic();

? ? }

? ? /**

? ? * 密碼處理

? ? * @param auth

? ? * @throws Exception

? ? */

? ? @Override

? ? public void configure(AuthenticationManagerBuilder auth) throws Exception {

? ? ? ? auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);

? ? }

? ? /**

? ? * 配置哪些請(qǐng)求不攔截

? ? * @param web

? ? * @throws Exception

? ? */

? ? @Override

? ? public void configure(WebSecurity web) throws Exception {

//? ? ? ? web.ignoring().antMatchers("/api/**",

//? ? ? ? ? ? ? ? "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"

//? ? ? ? ? ? ? );

? ? ? ? web.ignoring().antMatchers("/*/**");

? ? }

}

5、創(chuàng)建認(rèn)證授權(quán)相關(guān)的工具類

(1)DefaultPasswordEncoder:密碼處理的方法

@Component

public class DefaultPasswordEncoder implements PasswordEncoder {

? ? public DefaultPasswordEncoder() {

? ? ? ? this(-1);

? ? }

? ? public DefaultPasswordEncoder(int strength) {}

? ? public String encode(CharSequence rawPassword) {

? ? ? ? return MD5.encrypt(rawPassword.toString());

? ? }

? ? public boolean matches(CharSequence rawPassword, String encodedPassword) {

? ? ? ? return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));

? ? }

}

(2)TokenManager:token操作的工具類

@Component

public class TokenManager {

? ? private long tokenExpiration = 24*60*60*1000;

? ? private String tokenSignKey = "123456";

? ? public String createToken(String username) {

? ? ? ? String token = Jwts.builder().setSubject(username)

? ? ? ? ? ? ? ? .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))

? ? ? ? ? ? ? ? .signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();

? ? ? ? return token;

? ? }

? ? public String getUserFromToken(String token) {

? ? ? ? String user = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();

? ? ? ? return user;

? ? }

? ? public void removeToken(String token) {

? ? ? ? //jwttoken無(wú)需刪除,客戶端扔掉即可。

? ? }

}

(3)TokenLogoutHandler:退出實(shí)現(xiàn)

public class TokenLogoutHandler implements LogoutHandler {

? ? private TokenManager tokenManager;

? ? private RedisTemplate redisTemplate;

? ? public TokenLogoutHandler(TokenManager tokenManager, RedisTemplate redisTemplate) {

? ? ? ? this.tokenManager = tokenManager;

? ? ? ? this.redisTemplate = redisTemplate;

? ? }

? ? @Override

? ? public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {

? ? ? ? String token = request.getHeader("token");

? ? ? ? if (token != null) {

? ? ? ? ? ? tokenManager.removeToken(token);

? ? ? ? ? ? //清空當(dāng)前用戶緩存中的權(quán)限數(shù)據(jù)

? ? ? ? ? ? String userName = tokenManager.getUserFromToken(token);

? ? ? ? ? ? redisTemplate.delete(userName);

? ? ? ? }

? ? ? ? ResponseUtil.out(response, R.ok());

? ? }

}

(4)UnauthorizedEntryPoint:未授權(quán)統(tǒng)一處理

public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {

? ? @Override

? ? public void commence(HttpServletRequest request, HttpServletResponse response,

? ? ? ? ? ? ? ? ? ? ? ? AuthenticationException authException) throws IOException, ServletException {

? ? ? ? ResponseUtil.out(response, R.error());

? ? }

}

6、創(chuàng)建認(rèn)證授權(quán)實(shí)體類

(1)SecutityUser

package com.atguigu.serurity.entity;

import lombok.Data;

import lombok.extern.slf4j.Slf4j;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.userdetails.UserDetails;

import org.springframework.util.StringUtils;

import java.util.ArrayList;

import java.util.Collection;

import java.util.List;

/**

* <p>

* 安全認(rèn)證用戶詳情信息

* </p>

*

* @author qy

* @since 2019-11-08

*/

@Data

@Slf4j

public class SecurityUser implements UserDetails {

? ? //當(dāng)前登錄用戶

? ? private transient User currentUserInfo;

? ? //當(dāng)前權(quán)限

? ? private List<String> permissionValueList;

? ? public SecurityUser() {

? ? }

? ? public SecurityUser(User user) {

? ? ? ? if (user != null) {

? ? ? ? ? ? this.currentUserInfo = user;

? ? ? ? }

? ? }

? ? @Override

? ? public Collection<? extends GrantedAuthority> getAuthorities() {

? ? ? ? Collection<GrantedAuthority> authorities = new ArrayList<>();

? ? ? ? for(String permissionValue : permissionValueList) {

? ? ? ? ? ? if(StringUtils.isEmpty(permissionValue)) continue;

? ? ? ? ? ? SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);

? ? ? ? ? ? authorities.add(authority);

? ? ? ? }

? ? ? ? return authorities;

? ? }

? ? @Override

? ? public String getPassword() {

? ? ? ? return currentUserInfo.getPassword();

? ? }

? ? @Override

? ? public String getUsername() {

? ? ? ? return currentUserInfo.getUsername();

? ? }

? ? @Override

? ? public boolean isAccountNonExpired() {

? ? ? ? return true;

? ? }

? ? @Override

? ? public boolean isAccountNonLocked() {

? ? ? ? return true;

? ? }

? ? @Override

? ? public boolean isCredentialsNonExpired() {

? ? ? ? return true;

? ? }

? ? @Override

? ? public boolean isEnabled() {

? ? ? ? return true;

? ? }

}

(2)User用戶實(shí)體類:存儲(chǔ)用戶信息

package com.atguigu.serurity.entity;

import io.swagger.annotations.ApiModel;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import java.io.Serializable;

/**

* <p>

* 用戶實(shí)體類

* </p>

*

* @author qy

* @since 2019-11-08

*/

@Data

@ApiModel(description = "用戶實(shí)體類")

public class User implements Serializable {

????private static final long serialVersionUID = 1L;

????@ApiModelProperty(value = "微信openid")

????private String username;

????@ApiModelProperty(value = "密碼")

????private String password;

????@ApiModelProperty(value = "昵稱")

????private String nickName;

????@ApiModelProperty(value = "用戶頭像")

????private String salt;

????@ApiModelProperty(value = "用戶簽名")

????private String token;

}

7、創(chuàng)建認(rèn)證和授權(quán)的filter

(1)TokenLoginFilter:認(rèn)證的filter

package com.atguigu.serurity.filter;

import com.atguigu.commonutils.R;

import com.atguigu.commonutils.ResponseUtil;

import com.atguigu.serurity.entity.SecurityUser;

import com.atguigu.serurity.entity.User;

import com.atguigu.serurity.security.TokenManager;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.security.authentication.AuthenticationManager;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import org.springframework.security.core.Authentication;

import org.springframework.security.core.AuthenticationException;

import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

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.ArrayList;

/**

* <p>

* 登錄過(guò)濾器,繼承UsernamePasswordAuthenticationFilter,對(duì)用戶名密碼進(jìn)行登錄校驗(yàn)

* </p>

*

* @author qy

* @since 2019-11-08

*/

public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

? ? private AuthenticationManager authenticationManager;

? ? private TokenManager tokenManager;

? ? private RedisTemplate redisTemplate;

? ? public TokenLoginFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, RedisTemplate redisTemplate) {

? ? ? ? this.authenticationManager = authenticationManager;

? ? ? ? this.tokenManager = tokenManager;

? ? ? ? this.redisTemplate = redisTemplate;

? ? ? ? this.setPostOnly(false);

????????//設(shè)置登錄請(qǐng)求地址,這個(gè)地址是由spring security做到的,因此這個(gè)地址理論上可以隨便寫(xiě)

? ? ? ? this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login","POST"));

? ? }


? ? //得到輸入的用戶名和密碼

? ? @Override

? ? public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)

? ? ? ? ? ? throws AuthenticationException {

? ? ? ? try {

? ? ? ? ? ? User user = new ObjectMapper().readValue(req.getInputStream(), User.class);

? ? ? ? ? ? return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));

? ? ? ? } catch (IOException e) {

? ? ? ? ? ? throw new RuntimeException(e);

? ? ? ? }

? ? }


? ? /**

? ? * 登錄成功

? ? * @param req

? ? * @param res

? ? * @param chain

? ? * @param auth

? ? * @throws IOException

? ? * @throws ServletException

? ? */

? ? @Override

? ? protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Authentication auth) throws IOException, ServletException {

? ? ? ? SecurityUser user = (SecurityUser) auth.getPrincipal();

? ? ? ? String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());

? ? ? ? redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(), user.getPermissionValueList());

? ? ? ? ResponseUtil.out(res, R.ok().data("token", token));

? ? }

? ? /**

? ? * 登錄失敗

? ? * @param request

? ? * @param response

? ? * @param e

? ? * @throws IOException

? ? * @throws ServletException

? ? */

? ? @Override

? ? protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? AuthenticationException e) throws IOException, ServletException {

? ? ? ? ResponseUtil.out(response, R.error());

? ? }

}

(2)TokenAuthenticationFilter:

授權(quán)filter

package com.atguigu.serurity.filter;

import com.atguigu.commonutils.R;

import com.atguigu.commonutils.ResponseUtil;

import com.atguigu.serurity.security.TokenManager;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.security.authentication.AuthenticationManager;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.context.SecurityContextHolder;

import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import org.springframework.util.StringUtils;

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.ArrayList;

import java.util.Collection;

import java.util.List;

/**

* <p>

* 訪問(wèn)過(guò)濾器

* </p>

*

* @author qy

* @since 2019-11-08

*/

public class TokenAuthenticationFilter extends BasicAuthenticationFilter {

? ? private TokenManager tokenManager;

? ? private RedisTemplate redisTemplate;

? ? public TokenAuthenticationFilter(AuthenticationManager authManager, TokenManager tokenManager,RedisTemplate redisTemplate) {

? ? ? ? super(authManager);

? ? ? ? this.tokenManager = tokenManager;

? ? ? ? this.redisTemplate = redisTemplate;

? ? }

? ? @Override

? ? protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)

? ? ? ? ? ? throws IOException, ServletException {

? ? ? ? logger.info("================="+req.getRequestURI());

? ? ? ? if(req.getRequestURI().indexOf("admin") == -1) {

? ? ? ? ? ? chain.doFilter(req, res);

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? UsernamePasswordAuthenticationToken authentication = null;

? ? ? ? try {

? ? ? ? ? ? authentication = getAuthentication(req);

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? ResponseUtil.out(res, R.error());

? ? ? ? }

? ? ? ? if (authentication != null) {

? ? ? ? ? ? SecurityContextHolder.getContext().setAuthentication(authentication);

? ? ? ? } else {

? ? ? ? ? ? ResponseUtil.out(res, R.error());

? ? ? ? }

? ? ? ? chain.doFilter(req, res);

? ? }


? ? private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {

? ? ? ? // token置于header里

? ? ? ? String token = request.getHeader("token");

? ? ? ? if (token != null && !"".equals(token.trim())) {

? ? ? ? ? ? String userName = tokenManager.getUserFromToken(token);

? ? ? ? ? ? List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(userName);

? ? ? ? ? ? Collection<GrantedAuthority> authorities = new ArrayList<>();

? ? ? ? ? ? for(String permissionValue : permissionValueList) {

? ? ? ? ? ? ? ? if(StringUtils.isEmpty(permissionValue)) continue;

? ? ? ? ? ? ? ? SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);

? ? ? ? ? ? ? ? authorities.add(authority);

? ? ? ? ? ? }

? ? ? ? ? ? if (!StringUtils.isEmpty(userName)) {

? ? ? ? ? ? ? ? return new UsernamePasswordAuthenticationToken(userName, token, authorities);

? ? ? ? ? ? }

? ? ? ? ? ? return null;

? ? ? ? }

? ? ? ? return null;

? ? }

}



02-創(chuàng)建查詢用戶類和前端對(duì)接

一、創(chuàng)建自定義查詢用戶類

(1)在service_acls模塊創(chuàng)建,因?yàn)槠渌0宀粫?huì)用到。

查詢登錄和用戶權(quán)限類:UserDetailsServiceImpl ?implements UserDetailsService

二、后端接口和前端頁(yè)面對(duì)接

1、在前端項(xiàng)目中下載依賴

npm install --save vuex-persistedstate

2、替換相關(guān)文件

3、在node_modules文件夾中替換element-ui依賴

(1)修改router文件夾里面index. js 里面路徑和vue文件地址。

(2)修改數(shù)據(jù)庫(kù)菜單表路徑和頁(yè)面地址。

(3)修改前端項(xiàng)目請(qǐng)求地址是網(wǎng)關(guān)地址。

測(cè)試:


輸入用戶名和密碼:

權(quán)限管理整合springsecuri ty代碼執(zhí)行過(guò)程:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容