day18项目【权限管理和配置服务】-01 整合Spring Security

01-整合Spring Security

一、Spring Security介绍

1、框架介绍

Spring 是一个非常流行和成功的Java应用开发框架。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证Authentication)和用户授权(Authorization两个部分。

(1)用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。

(2)用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。

Spring Security其实就是用filter,多请求的路径进行过滤。

(1)如果是基于Session,那么Spring-security会对cookie里的sessionid进行解析,找到服务器存储的sesion信息,然后判断当前用户是否符合请求的要求。

(2)如果是token,则是解析出token,然后将当前请求加入到Spring-security管理的权限信息中去

2、认证与授权实现思路

如果系统的模块众多,每个模块都需要就行授权与认证,所以我们选择基于token的形式进行授权与认证,用户根据用户名密码认证成功,然后获取当前用户角色的一系列权限值,并以用户名为key,权限列表为value的形式存入redis缓存中,根据用户名相关信息生成token返回,浏览器将token记录到cookie中每次调用api接口都默认将token携带到header请求头中Spring-security解析header头获取token信息,解析token获取当前用户名,根据用户名就可以从redis中获取权限列表,这样Spring-security就能够判断当前请求是否有权限访问

二、整合Spring Security

1、在common下创建spring_security模块

2、在spring_security引入相关依赖

    <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需要使用到jwt

        </dependency>

    </dependencies>

3、在service_acls引入spring_security依赖

        <dependency>

            <groupId>com.atguigu</groupId>

            <artifactId>spring_security</artifactId>

            <version>0.0.1-SNAPSHOT</version>

        </dependency>

4、复制工具类到common_utils

5、创建spring security核心配置类

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

这个配置指明了用户名密码的处理方式、请求路径的开合、登录登出控制等和安全相关的配置。

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;

    }

    /**

    * 配置设置

    * @param http

    * @throws Exception

    */

    @Override

    protected void configure(HttpSecurity http) throws Exception {

        http.exceptionHandling()

                .authenticationEntryPoint(new UnauthorizedEntryPoint())

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

                .authorizeRequests()

                .anyRequest().authenticated()

                //设置退出请求地址,这个地址是由spring security做到的,因此这个地址理论上可以随便写

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

    }

    /**

    * 配置哪些请求不拦截

    * @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、创建认证授权相关的工具类

(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无需删除,客户端扔掉即可。

    }

}

(3)TokenLogoutHandler:退出实现

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);

            //清空当前用户缓存中的权限数据

            String userName = tokenManager.getUserFromToken(token);

            redisTemplate.delete(userName);

        }

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

    }

}

(4)UnauthorizedEntryPoint:未授权统一处理

public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {

    @Override

    public void commence(HttpServletRequest request, HttpServletResponse response,

                        AuthenticationException authException) throws IOException, ServletException {

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

    }

}

6、创建认证授权实体类

(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>

* 安全认证用户详情信息

* </p>

*

* @author qy

* @since 2019-11-08

*/

@Data

@Slf4j

public class SecurityUser implements UserDetails {

    //当前登录用户

    private transient User currentUserInfo;

    //当前权限

    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用户实体类:存储用户信息

package com.atguigu.serurity.entity;

import io.swagger.annotations.ApiModel;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import java.io.Serializable;

/**

* <p>

* 用户实体类

* </p>

*

* @author qy

* @since 2019-11-08

*/

@Data

@ApiModel(description = "用户实体类")

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、创建认证和授权的filter

(1)TokenLoginFilter:认证的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>

* 登录过滤器,继承UsernamePasswordAuthenticationFilter,对用户名密码进行登录校验

* </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);

        //设置登录请求地址,这个地址是由spring security做到的,因此这个地址理论上可以随便写

        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:

授权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>

* 访问过滤器

* </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-创建查询用户类和前端对接

一、创建自定义查询用户类

(1)在service_acls模块创建,因为其他模板不会用到。

查询登录和用户权限类:UserDetailsServiceImpl  implements UserDetailsService

二、后端接口和前端页面对接

1、在前端项目中下载依赖

npm install --save vuex-persistedstate

2、替换相关文件

3、在node_modules文件夹中替换element-ui依赖

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

(2)修改数据库菜单表路径和页面地址。

(3)修改前端项目请求地址是网关地址。

测试:


输入用户名和密码:

权限管理整合springsecuri ty代码执行过程:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345