- 首先是使用的SpringBoot框架
基础需要的pom以来如下,基础的springboot项目的创建就不一一赘述了。
<!--spring web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--jpa 对数据库操作的框架 类似mybatis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--页面引擎 thymeleaf模板--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--mysql数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
- 加入必须的security依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
数据库连接配置文件
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/springsecurity?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=user spring.datasource.password=password
#设置运行时打印sql语句 spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update #关闭thymeleaf缓存 spring.thymeleaf.cache=false
#不用数据库认证时默认的用户名和密码
#spring.security.user.name=admin
#spring.security.user.password=1234
- 实体类***********重要***********
对于实体类的创建:
- 用户表包含权限表
- 用户表不包含权限表
笔者使用的是用户表包含权限表的方式,那么不包含权限表该怎么解决呢?
创建一个新的实体类,如SecurityUser 继承User 实现UserDetails 它包含User的内容和UserDetails的内容,对于权限的设置就不一样了
重点注意这个方法 getAuthorities ,它来将权限交给security管理 暂时可以用Null值 在UserDetailsService中具体赋值
@Entity public class User implements Serializable, UserDetails { private String username; private String password; private String role; public User(){} public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } /** * 指示用户的账户是否已过期。无法验证过期的账户。 * 如果用户的账户有效(即未过期),则返回true,如果不在有效就返回false */ @Override public boolean isAccountNonExpired() { return true; } /** * 指示用户是锁定还是解锁。无法对锁定的用户进行身份验证。 * 如果用户未被锁定,则返回true,否则返回false */ @Override public boolean isAccountNonLocked() { return true; } /** * 指示用户的凭证(密码)是否已过期。过期的凭证阻止身份验证 * 如果用户的凭证有效(即未过期),则返回true * 如果不在有效(即过期),则返回false */ @Override public boolean isCredentialsNonExpired() { return true; } /** * 指示用户是启用还是禁用。无法对禁用的用户进行身份验证 * 如果启用了用户,则返回true,否则返回false */ @Override public boolean isEnabled() { return true; } /** * 得到用户的权限,如果权限表和用户表是分开的,我们需要在重新定义一个实体类实现UserDetails 并且继承于User类
* 交给security的权限,放在UserDetailService进行处理 */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); //角色必须以ROLE_开头,如果数据库没有,则需要加上 至于角色为什么必须要以ROLE_开头 笔者没有进行深究 authorities.add(new SimpleGrantedAuthority("ROLE_"+this.role)); return authorities; } }
- 此处就先不谈security的config配置吧,后面会提到
我们还没有用到数据库中的数据,在此我们需要改造UserServiceImpl类,让其实现 UserDetailsService 并重写其中的 loadUserByUsername 方法,这是数据库认证的必要流程,贴代码:
@Service public class UserServiceImpl implements UserService, UserDetailsService { @Autowired UserDao userDao; /** * 实现security认证实现的方法 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { logger.error("username:" + username); //option这个东西请读者自行研究,此处可以直接是一个user,optional只是用于判断空值,避开空指针异常的 Optional<User> byUsername = userDao.findByUsername(username); if (!byUsername.isPresent()) { throw new UsernameNotFoundException("用户名不存在,请先注册后登录!"); }else{ //权限表和用户表分开************按照下面方式,如果不是,直接返回带有权限信息的User对象 //查询的权限在此处可以通过数据库查询,并且赋值 //List<GrantedAuthority> authorities=new ArrayList<>(); //authorities.add(new SimpleGrantedAuthority("ROLE_SPITTER")) //新创建一个SecurityUser(自定义实现了UserDetails的类) //将authorites放到该对象中,并返回出去 return byUsername.get(); } } }
- 对于security的配置******************重要
新创建一个配置类继承 WebSecurityConfigurerAdapter
需要注意的几个方法(重写的方法):
configure(HttpSecurity http) : htpp请求安全处理
configure(AuthenticationManagerBuilder auth) : 自定义数据库权限认证
configure(WebSecurity web) : WEB安全
@Configuration @EnableWebSecurity // 开启注解 @EnableGlobalMethodSecurity(prePostEnabled = true) // 开启注解 //自定义了一个springSecurity安全框架的配置类 继承WebSecurityConfigurerAdapter,重写其中的方法configure, public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserServiceImpl userService; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() //接触防止跨域请求 .authorizeRequests() .antMatchers("/toLogin","/templates/**","/logout","/toIndex").permitAll() //忽略认证的url .anyRequest().authenticated() //其他的url都需要被认证才可以访问 .and() .formLogin() //允许自定义表单登录 .loginPage("/toLogin") //这是action地址 不能写具体页面 确定自定义登录页 自己不需要写login方法 登录是由security提供 .loginProcessingUrl("/login") //这是html中form中action的值 必须要对应 .defaultSuccessUrl("/toIndex") //默认登录成功会跳转的controller //关于登录失败的提示信息,请读者自行解决 .failureForwardUrl("/login-error") .failureUrl("/login-error"); } //密码加密,最新的security必须使用密码加密 @Bean public PasswordEncoder passwordEncoder(){ //使用BCcypt加密 return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //从数据库读取的用户进行身份认证 实现了userDetailsService的类 auth.userDetailsService(userService) .passwordEncoder(passwordEncoder()); } //解除对静态资源的保护 @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/js/**","/templates/**"); } }
- 接下来是Controller和页面
- 必须要登录成功才可以访问其他的接口,没有通过认证会一直卡在login
@Controller public class UserController { @Autowired UserServiceImpl userService; @RequestMapping("/toIndex") public String index(){ return "index"; } @RequestMapping("/toLogin") public String login(){ return "login"; } //定义需要访问的权限 @PreAuthorize("hasAnyRole('ROLE_admin')") @RequestMapping("/sayHello") @ResponseBody public String sayHello(){ return "hello"; } //定义需要访问的权限 @PreAuthorize("hasAnyAuthority('ROLE_user')") @RequestMapping("/sayYes") @ResponseBody public String sayYes(){ return "yes"; } }
html页面
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <!--layui css--> <link rel="stylesheet" th:href="@{js/layui-v2.5.5/layui/css/layui.css}"> <!--layui js--> <script th:src="@{js/layui-v2.5.5/layui/layui.js}"></script> <!--jquery--> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <title>登录页面</title> </head> <body> <!--这里请注意action的值需要与loginProcessingUrl 相对应--> <form action="/login" method="post"> <div class="layui-form-item"> <label class="layui-form-label">输入框</label> <div class="layui-input-inline"> <input type="text" id="username" name="username" required lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">密码框</label> <div class="layui-input-inline"> <input type="password" name="password" required lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <div class="layui-input-block"> <button class="layui-btn layui-btn-warm" lay-submit lay-filter="formDemo">登录</button> </div> </div> </form> </body> </html>
第一次写博客 感觉还是有点乱,请和谐讨论,笔者也是看了很多其他的博客慢慢弄出来的,希望大家不要害怕麻烦,慢慢来,引入就不贴了,看了太多 也不知道谁是谁的