Spring安全框架——jwt的集成(服务器端)

新建用户表——users,并完成数据库增删查改

一、新建表

Spring安全框架——jwt的集成(服务器端)

二、持久化映射,建立模型类,添加主键生成器

//指定生成器名称
    @GeneratedValue(generator = "uuid2" )
    @GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator" )

三、建立Dao层

package edu.ynmd.cms.dao;

import edu.ynmd.cms.model.Users;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import javax.websocket.server.PathParam;
import java.util.List;

public interface UsersDao extends JpaRepository<Users,String> {

    @Query("select u from Users u where u.username=:username and u.pass=:pass")
    List<Users> getUsersByUsernameAndPass(@Param("username")String username, @Param("pass") String pass);

}

四、配置service层

//用户表
    Users saveUser(Users users);
    boolean deleteUser(String id);
    Users getUser(String id);
    Users getUserByUserNameAndPass(String username,String pass);
  • 在ManageServiceImpl添加方法具体实现
//*****************************用户表增删查改开始*********************************
    @Override
    public Users saveUser(Users users) {
        try {
            return usersDao.save(users);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public boolean deleteUser(String id) {
        try {
            usersDao.deleteById(id);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    @Override
    public Users getUser(String id) {
        Optional<Users> temp=usersDao.findById(id);
        return temp.isPresent()?temp.get():null;
    }

    @Override
    public Users getUserByUserNameAndPass(String username, String pass) {
        List<Users> ul=usersDao.getUsersByUsernameAndPass(username,pass);
        if(ul.size()>0){
            return ul.get(0);
        }
        return null;
    }

JWT框架的搭建

一、添加Maven依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>

二、编写JwtUtil工具类(生成token,解析token,校验token)

package edu.ynmd.cms.tools;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtUtil {
    private static final Logger logger= LoggerFactory.getLogger(JwtUtil.class);
    public static  final long EXPIRATION_TIME=60*60*1000;// 令牌环有效期
    public static final String SECRET="abc123456def";//令牌环密钥
    public static final String TOKEN_PREFIX="Bearer";//令牌环头标识
    public static final String HEADER_STRING="Passport";//配置令牌环在http heads中的键值
    public static final String ROLE="ROLE";//自定义字段-角色字段

    //生成令牌环
    public static String generateToken(String userRole,String userid){
        HashMap<String,Object> map=new HashMap<>();
        map.put(ROLE,userRole);
        map.put("userid",userid);
        String jwt= Jwts.builder()
                .setClaims(map)
                .setExpiration(new Date(System.currentTimeMillis()+EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512,SECRET)
                .compact();
        return TOKEN_PREFIX+" "+jwt;
    }
    //生成令牌环
    public static String generateToken(String userRole,String userid,long exprationtime){
        HashMap<String,Object> map=new HashMap<>();
        map.put(ROLE,userRole);
        map.put("userid",userid);
        String jwt= Jwts.builder()
                .setClaims(map)
                .setExpiration(new Date(System.currentTimeMillis()+exprationtime))
                .signWith(SignatureAlgorithm.HS512,SECRET)
                .compact();
        return TOKEN_PREFIX+" "+jwt;
    }

    //令牌环校验
    public static Map<String,Object> validateTokenAndGetClaims(HttpServletRequest request){
        String token=request.getHeader(HEADER_STRING);
        if(token==null){
            throw new TokenValidationException("Missing Token");

        }
        else{
            Map<String,Object> body=Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX,""))
                    .getBody();
            return body;
        }
    }

    static class TokenValidationException extends RuntimeException{
        public TokenValidationException(String msg){
            super(msg);
        }
    }

}

三、集成JWT filter(拦截器/过滤器)——建立filter包

package edu.ynmd.cms.filter;

import edu.ynmd.cms.tools.JwtUtil;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

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.Arrays;
import java.util.Map;

import static edu.ynmd.cms.tools.JwtUtil.ROLE;

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private static final PathMatcher pathmatcher = new AntPathMatcher();
    private String[] protectUrlPattern = {"/manage/**", "/member/**", "/auth/**"}; //那些请求需要进行安全校验
    public JwtAuthenticationFilter() {

    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
//是不是可以在这里做多种方式登录呢


        try {
            if (isProtectedUrl(httpServletRequest)) {
                Map<String, Object> claims = JwtUtil.validateTokenAndGetClaims(httpServletRequest);
                String role = String.valueOf(claims.get(ROLE));
                String userid = String.valueOf(claims.get("userid"));
                //最关键的部分就是这里, 我们直接注入了
                SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
                        userid, null, Arrays.asList(() -> role)
                ));

            }
        } catch (Exception e) {
            e.printStackTrace();
            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
            return;
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);

    }

    //是否是保护连接
    private boolean isProtectedUrl(HttpServletRequest request) {

        boolean flag = false;
        for (int i = 0; i < protectUrlPattern.length; i++) {
            if (pathmatcher.match(protectUrlPattern[i], request.getServletPath())) {
                return true;
            }
        }
        return false;
    }


}

四、配置JWT config类

package edu.ynmd.cms.config;

import edu.ynmd.cms.filter.JwtAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .cors()  //允许跨域访问
                .and()
                .authorizeRequests()
                .antMatchers("/").authenticated() //配置那些url需要进行校验--所有请求都需要校验"/"
                .antMatchers("/public/**").permitAll() //那些请求不需要校验
                .anyRequest().authenticated() //自定义校验类
                .and()
                .addFilterBefore(new JwtAuthenticationFilter(),
                        UsernamePasswordAuthenticationFilter.class)
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)//关闭session
        ;
    }

}

五、添加Action注解——在AdminAction添加

@CrossOrigin
@RestController
@PreAuthorize("hasAuthority('admin')") //配置角色,拥有该角色的用户方可访问
@RequestMapping("/manage")

六、登录Action——在PublicAction添加

//用户登录方法
    @PostMapping("/login")
    @ResponseBody
    public HashMap<String,String> login(
            @RequestBody Account account) throws IOException {
        Users u=manageService.getUserByUserNameAndPass(account.username,account.password);
        //if(account.username.equals("admin")&&account.password.equals("123456")){
        if(u!=null){
            //String jwt= JwtUtil.generateToken("admin","123456789abc");
            String jwt= JwtUtil.generateToken(u.getRoleid(),u.getUsersid());


            return new HashMap<String,String>(){{
                put("msg","ok");
                put("token",jwt);
                put("role",u.getRoleid());
                // put("role","admin");
            }};
        }
        else {
            //return new ResponseEntity(HttpStatus.UNAUTHORIZED);
            return new HashMap<String,String>(){{
                put("msg","error");
                put("token","error");
            }};
        }
    }

    public static class Account{
        public String username;
        public String password;
    }

七、token令牌环,访问需要验证的资源,新建service工具类

  • 在tools中添加isNullOrSpace方法进行判断
public static boolean isNullOrSpace(String value){
        if(value==null){
            return true;
        }
        else {
            if(value.equals("")){
                return true;
            }
            else {
                return false;
            }
        }

    }
  • 在ManageService添加方法
String getCurrentUserId();
String getCurrentRole();
  • 在ManageServiceImpl添加方法具体实现
//获取当前登录用的的Id
    @Override
    public String getCurrentUserId() {
        String userid= (String) SecurityContextHolder.getContext().getAuthentication() .getPrincipal();
        if(Tools.isNullOrSpace(userid)){
            return null;
        }
        else {
            return userid;
        }
    }
    //获取当前登录用户的角色
    @Override
    public String getCurrentRole() {
        String role=null;
        Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>)    SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        for (GrantedAuthority authority : authorities) {
            role = authority.getAuthority();

        }

        if(Tools.isNullOrSpace(role)){
            return null;
        }
        else{
            return role;
        }
    }

八、识别token令牌环信息——在AdminAction添加

@GetMapping("testSecurityResource")
    @ResponseBody
    public String testSecurityResource() throws Exception{

        String userid=manageService.getCurrentUserId();
        String role=manageService.getCurrentRole();

        return "受保护的资源,当前用户的id是"+userid+"当前用户的角色是"+role;

    }

九、自动更新令牌环,新建AuthAction类

package edu.ynmd.cms.action;

import edu.ynmd.cms.tools.JwtUtil;
import edu.ynmd.cms.tools.Tools;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.HashMap;
//令牌环更新
@CrossOrigin
@RestController
@PreAuthorize("hasAuthority('admin')")
@RequestMapping("/auth")
public class AuthAction {
    //更新令牌环信息
    @GetMapping("refreshToken")
    @ResponseBody
    public HashMap<String,String> refreshToken( HttpServletRequest request){
        String role=null;
        Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>)    SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        for (GrantedAuthority authority : authorities) {
            role = authority.getAuthority();

        }
        // UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication() .getPrincipal();
        String userid= (String)SecurityContextHolder.getContext().getAuthentication() .getPrincipal();
        if(Tools.isNullOrSpace(role)){
            return new HashMap<String,String>(){{
                put("token","error");
            }};
        }
        else{

            String jwt="";

            jwt= JwtUtil.generateToken(role,userid,60*60*1000);//一小时
            HashMap<String,String> m=new HashMap<>();
            m.put("token",jwt);
            return m;

        }

    }

    //获取当前登录用户的角色
    @GetMapping("getRole")
    @ResponseBody
    public HashMap<String,String> getRoleByToken(){

        String role="";
        String userid="";
        Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>)    SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        for (GrantedAuthority authority : authorities) {
            role = authority.getAuthority();

        }
        if(Tools.isNullOrSpace(role)){
            return new HashMap<String,String>(){{
                put("role","error");
            }};
        }
        else{

            HashMap<String,String> m=new HashMap<>();
            m.put("role",role);
            return m;
        }

    }
}

最后用postman进行测试

Spring安全框架——jwt的集成(服务器端)

结果

Spring安全框架——jwt的集成(服务器端)

原文 

https://segmentfault.com/a/1190000022424836

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Spring安全框架——jwt的集成(服务器端)

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址